From 94b48b0ebc290f21b7a8d47311b89fb902b3096b Mon Sep 17 00:00:00 2001 From: Frederic Brin Date: Sat, 17 Sep 2022 12:48:26 +0200 Subject: [PATCH 1/3] Pass all code through black formatter --- docs/conf.py | 111 +- docs/ilodoc.py | 26 +- hpilo.py | 2295 +++++++++++++++++++++----------- hpilo_fw.py | 62 +- setup.py | 41 +- tests/test_boot.py | 4 +- tests/test_delayed.py | 6 +- tests/test_global_settings.py | 17 +- tests/test_languages.py | 6 +- tests/test_logs.py | 4 +- tests/test_network_settings.py | 16 +- tests/test_requests.py | 29 +- tests/test_responses.py | 34 +- tests/test_snmp.py | 16 +- tests/test_uid.py | 8 +- tests/test_users.py | 62 +- tests/utils.py | 61 +- 17 files changed, 1813 insertions(+), 985 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index d5d80124..341b2077 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,137 +17,136 @@ # 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. -sys.path.insert(0, os.path.abspath('..')) -sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath("..")) +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. -extensions = ['sphinx.ext.autodoc', 'ilodoc'] +extensions = ["sphinx.ext.autodoc", "ilodoc"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +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' +master_doc = "index" # General information about the project. -project = u'python-hpilo' -copyright = u'2011-2020, Dennis Kaarsemaker' +project = u"python-hpilo" +copyright = u"2011-2020, Dennis Kaarsemaker" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '4.4.3' +version = "4.4.3" # The full version, including alpha/beta/rc tags. -release = '4.4.3' +release = "4.4.3" # 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'] +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' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # 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 = [theme.get_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 = '_static/python-hpilo.png' +html_logo = "_static/python-hpilo.png" # 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, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +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 = False @@ -156,57 +155,62 @@ html_show_sphinx = False # 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 = 'python-hpilodoc' +htmlhelp_basename = "python-hpilodoc" # -- 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]). latex_documents = [ - ('index', 'python-hpilo.tex', u'python-hpilo Documentation', - u'Dennis Kaarsemaker', 'manual'), + ( + "index", + "python-hpilo.tex", + u"python-hpilo Documentation", + u"Dennis Kaarsemaker", + "manual", + ), ] # 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 -------------------------------------------- @@ -214,6 +218,5 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'python-hpilo', u'python-hpilo Documentation', - [u'Dennis Kaarsemaker'], 1) + ("index", "python-hpilo", u"python-hpilo Documentation", [u"Dennis Kaarsemaker"], 1) ] diff --git a/docs/ilodoc.py b/docs/ilodoc.py index 64ceb073..50fc40d5 100644 --- a/docs/ilodoc.py +++ b/docs/ilodoc.py @@ -5,8 +5,10 @@ import re import hpilo + def setup(app): - app.add_directive('ilo_output', OutputDirective) + app.add_directive("ilo_output", OutputDirective) + class OutputDirective(Directive): required_arguments = 1 @@ -14,24 +16,24 @@ class OutputDirective(Directive): def run(self): method = self.arguments[0] - if '#' in method: - method, suffix = method.split('#') - suffix = '_' + suffix + if "#" in method: + method, suffix = method.split("#") + suffix = "_" + suffix else: - suffix = '' - assert re.match('^[a-zA-Z][a-zA-Z0-9_]*$', method) + suffix = "" + assert re.match("^[a-zA-Z][a-zA-Z0-9_]*$", method) srcdir = self.state.document.settings.env.srcdir - with open(os.path.join(srcdir, 'output', method + suffix)) as fd: + with open(os.path.join(srcdir, "output", method + suffix)) as fd: content = fd.read() - if '\n\n' in content: - params, result = content.split('\n\n') - params = ', '.join(params.split('\n')) + if "\n\n" in content: + params, result = content.split("\n\n") + params = ", ".join(params.split("\n")) else: - params, result = '', content + params, result = "", content out = ">>> ilo.%s(%s)\n%s" % (method, params, result) literal = nodes.literal_block(out, out) - literal['language'] = 'python' + literal["language"] = "python" set_source_info(self, literal) self.state.parent.children[-1].children[-1].append(literal) return [] diff --git a/hpilo.py b/hpilo.py index 78c5b31f..ef1e76be 100644 --- a/hpilo.py +++ b/hpilo.py @@ -22,16 +22,20 @@ PY3 = sys.version_info[0] >= 3 if PY3: import urllib.request as urllib2 - class Bogus(Exception): pass + + class Bogus(Exception): + pass + socket.sslerror = Bogus basestring = str from os import fsencode else: import urllib2 + fsencode = lambda x: x # Python 2.7.13 renamed PROTOCOL_SSLv23 to PROTOCOL_TLS -if not hasattr(ssl, 'PROTOCOL_TLS'): +if not hasattr(ssl, "PROTOCOL_TLS"): ssl.PROTOCOL_TLS = ssl.PROTOCOL_SSLv23 # Oh the joys of monkeypatching... @@ -39,44 +43,56 @@ class Bogus(Exception): pass # - We need to disable escaping of the PASSWORD attribute, because iLO doesn't # unescape it properly def CDATA(text=None): - element = etree.Element('![CDATA[') + element = etree.Element("![CDATA[") element.text = text return element + # Adding this tag to RIBCL scripts should make this hack unnecessary in newer # iLO firmware versions. TODO: Check compatibility. # class DoNotEscapeMe(str): pass + etree._original_escape_attrib = etree._escape_attrib + + def _escape_attrib(text, *args, **kwargs): if isinstance(text, DoNotEscapeMe): return str(text) else: return etree._original_escape_attrib(text, *args, **kwargs) + + etree._escape_attrib = _escape_attrib # Python 2.7 and 3 -if hasattr(etree, '_serialize_xml'): +if hasattr(etree, "_serialize_xml"): etree._original_serialize_xml = etree._serialize_xml + def _serialize_xml(write, elem, *args, **kwargs): - if elem.tag == '![CDATA[': + if elem.tag == "![CDATA[": write("\n<%s%s]]>\n" % (elem.tag, elem.text)) return return etree._original_serialize_xml(write, elem, *args, **kwargs) - etree._serialize_xml = etree._serialize['xml'] = _serialize_xml + + etree._serialize_xml = etree._serialize["xml"] = _serialize_xml # Python 2.6, and non-stdlib ElementTree -elif hasattr(etree.ElementTree, '_write'): +elif hasattr(etree.ElementTree, "_write"): etree.ElementTree._orig_write = etree.ElementTree._write + def _write(self, file, node, encoding, namespaces): - if node.tag == '![CDATA[': + if node.tag == "![CDATA[": file.write("\n\n" % node.text.encode(encoding)) else: self._orig_write(file, node, encoding, namespaces) + etree.ElementTree._write = _write else: - raise RuntimeError("Don't know how to monkeypatch XML serializer workarounds. Please report a bug at https://github.com/seveas/python-hpilo") + raise RuntimeError( + "Don't know how to monkeypatch XML serializer workarounds. Please report a bug at https://github.com/seveas/python-hpilo" + ) # We handle non-ascii characters in the returned XML by replacing them with XML # character references. This likely results in bogus data, but avoids crashes. @@ -89,128 +105,176 @@ def iloxml_replace(error): b = ord(b) if b < 128: break - ret += u'?' - warnings.warn("Invalid ascii data found: %s, replaced with %s" % (repr(error.object[error.start:pos]), ret), IloWarning) + ret += u"?" + warnings.warn( + "Invalid ascii data found: %s, replaced with %s" + % (repr(error.object[error.start : pos]), ret), + IloWarning, + ) return (ret, pos) -codecs.register_error('iloxml_replace', iloxml_replace) + + +codecs.register_error("iloxml_replace", iloxml_replace) # Which protocol to use -ILO_RAW = 1 +ILO_RAW = 1 ILO_HTTP = 2 ILO_LOCAL = 3 + class IloErrorMeta(type): def __new__(cls, name, parents, attrs): - if 'possible_messages' not in attrs: - attrs['possible_messages'] = [] - if 'possible_codes' not in attrs: - attrs['possible_codes'] = [] + if "possible_messages" not in attrs: + attrs["possible_messages"] = [] + if "possible_codes" not in attrs: + attrs["possible_codes"] = [] klass = super(IloErrorMeta, cls).__new__(cls, name, parents, attrs) - if name != 'IloError': + if name != "IloError": IloError.known_subclasses.append(klass) return klass + class IloError(Exception): __metaclass__ = IloErrorMeta + def __init__(self, message, errorcode=None): if issubclass(IloError, object): super(IloError, self).__init__(message) else: Exception.__init__(self, message) self.errorcode = errorcode + known_subclasses = [] + if PY3: # Python 3 ignores __metaclass__ but wants class foo(metaclass=bar) But # that syntax is an error on older python, so recreate IloError properly # the manual way. - IloError = IloErrorMeta('IloError', (Exception,), {'known_subclasses': [], '__init__': IloError.__init__}) + IloError = IloErrorMeta( + "IloError", + (Exception,), + {"known_subclasses": [], "__init__": IloError.__init__}, + ) + class IloCommunicationError(IloError): pass + class IloGeneratingCSR(IloError): - possible_messages = ['The iLO subsystem is currently generating a Certificate Signing Request(CSR), run script after 10 minutes or more to receive the CSR.'] + possible_messages = [ + "The iLO subsystem is currently generating a Certificate Signing Request(CSR), run script after 10 minutes or more to receive the CSR." + ] possible_codes = [0x0088] + class IloLoginFailed(IloError): - possible_messages = ['Login failed', 'Login credentials rejected'] - possible_codes = [0x005f] + possible_messages = ["Login failed", "Login credentials rejected"] + possible_codes = [0x005F] + class IloUserNotFound(IloError): - possible_codes = [0x000a] + possible_codes = [0x000A] + class IloPermissionError(IloError): possible_codes = [0x0023] + class IloNotARackServer(IloError): - possible_codes = [0x002a] + possible_codes = [0x002A] + class IloLicenseKeyError(IloError): - possible_codes = [0x002e] + possible_codes = [0x002E] + class IloFeatureNotSupported(IloError): - possible_codes = [0x003c] + possible_codes = [0x003C] + class IloNotConfigured(IloError): - possible_codes = [0x006d] + possible_codes = [0x006D] + class IloWarning(Warning): pass + class IloXMLWarning(Warning): pass + class IloTestWarning(Warning): pass + class Ilo(object): """Represents an iLO/iLO2/iLO3/iLO4/RILOE II management interface on a - specific host. A new connection using the specified login, password and - timeout will be made for each API call. The library will detect which - protocol to use, but you can override this by setting protocol to - ILO_RAW or ILO_HTTP. Use ILO_LOCAL to avoid using a network connection - and use hponcfg instead. Username and password are ignored for ILO_LOCAL - connections. Set delayed to True to make python-hpilo not send requests - immediately, but group them together. See :func:`call_delayed`""" + specific host. A new connection using the specified login, password and + timeout will be made for each API call. The library will detect which + protocol to use, but you can override this by setting protocol to + ILO_RAW or ILO_HTTP. Use ILO_LOCAL to avoid using a network connection + and use hponcfg instead. Username and password are ignored for ILO_LOCAL + connections. Set delayed to True to make python-hpilo not send requests + immediately, but group them together. See :func:`call_delayed`""" XML_HEADER = b'\r\n' HTTP_HEADER = b"POST /ribcl HTTP/1.1\r\nHost: localhost\r\nContent-Length: %d\r\nConnection: Close%s\r\n\r\n" HTTP_UPLOAD_HEADER = b"POST /cgi-bin/uploadRibclFiles HTTP/1.1\r\nHost: localhost\r\nConnection: Close\r\nContent-Length: %d\r\nContent-Type: multipart/form-data; boundary=%s\r\n\r\n" BLOCK_SIZE = 64 * 1024 - def __init__(self, hostname, login=None, password=None, timeout=60, port=443, protocol=None, delayed=False, ssl_verify=False, ssl_context=None, ssl_version=None): + def __init__( + self, + hostname, + login=None, + password=None, + timeout=60, + port=443, + protocol=None, + delayed=False, + ssl_verify=False, + ssl_context=None, + ssl_version=None, + ): self.hostname = hostname - self.login = login or 'Administrator' - self.password = password or 'Password' - self.timeout = timeout - self.debug = 0 - self.port = port + self.login = login or "Administrator" + self.password = password or "Password" + self.timeout = timeout + self.debug = 0 + self.port = port self.protocol = protocol self.ssl_verify = False self.ssl_context = ssl_context - self.cookie = None - self.delayed = delayed + self.cookie = None + self.delayed = delayed self._elements = None self._processors = [] self.save_response = None self.read_response = None self.save_request = None - self._protect_passwords = os.environ.get('HPILO_DONT_PROTECT_PASSWORDS', None) != 'YesPlease' + self._protect_passwords = ( + os.environ.get("HPILO_DONT_PROTECT_PASSWORDS", None) != "YesPlease" + ) self.firmware_mirror = None self.hponcfg = "/sbin/hponcfg" - hponcfg = 'hponcfg' - if platform.system() == 'Windows': - self.hponcfg = 'C:\Program Files\HP Lights-Out Configuration Utility\cpqlocfg.exe' - hponcfg = 'cpqlocfg.exe' - for path in os.environ.get('PATH','').split(os.pathsep): + hponcfg = "hponcfg" + if platform.system() == "Windows": + self.hponcfg = ( + "C:\Program Files\HP Lights-Out Configuration Utility\cpqlocfg.exe" + ) + hponcfg = "cpqlocfg.exe" + for path in os.environ.get("PATH", "").split(os.pathsep): maybe = os.path.join(path, hponcfg) if os.access(maybe, os.X_OK): self.hponcfg = maybe break if self.ssl_verify: - if sys.version_info < (2,7,9): - raise EnvironmentError("SSL verification only works with python 2.7.9 or newer") + if sys.version_info < (2, 7, 9): + raise EnvironmentError( + "SSL verification only works with python 2.7.9 or newer" + ) if not self.ssl_context: self.ssl_context = ssl.create_default_context() # Sadly, ancient iLO's aren't dead yet, so let's enable sslv3 by default @@ -220,35 +284,35 @@ def __str__(self): return "iLO interface of %s" % self.hostname def _debug(self, level, message): - if message.__class__.__name__ == 'bytes': - message = message.decode('ascii') + if message.__class__.__name__ == "bytes": + message = message.decode("ascii") if self.debug >= level: if self._protect_passwords: message = re.sub(r'PASSWORD=".*?"', 'PASSWORD="********"', message) sys.stderr.write(message) - if message.startswith('\r'): + if message.startswith("\r"): sys.stderr.flush() else: - sys.stderr.write('\n') + sys.stderr.write("\n") def _request(self, xml, progress=None): """Given an ElementTree.Element, serialize it and do the request. - Returns an ElementTree.Element containing the response""" + Returns an ElementTree.Element containing the response""" if not self.protocol and not self.read_response: self._detect_protocol() # Serialize the XML - if hasattr(etree, 'tostringlist'): - xml = b"\r\n".join(etree.tostringlist(xml)) + b'\r\n' + if hasattr(etree, "tostringlist"): + xml = b"\r\n".join(etree.tostringlist(xml)) + b"\r\n" else: xml = etree.tostring(xml) - header, data = self._communicate(xml, self.protocol, progress=progress) + header, data = self._communicate(xml, self.protocol, progress=progress) # This thing usually contains multiple XML messages messages = [] while data: - pos = data.find('', ILO_HTTP, save=False) + header, data = self._communicate( + b'', ILO_HTTP, save=False + ) if header: self.protocol = ILO_HTTP else: self.protocol = ILO_RAW def _upload_file(self, filename, progress): - with open(filename, 'rb') as fd: + with open(filename, "rb") as fd: firmware = fd.read() - boundary = b'------hpiLO3t%dz' % random.randint(100000,1000000) + boundary = b"------hpiLO3t%dz" % random.randint(100000, 1000000) while boundary in firmware: - boundary = b'------hpiLO3t%dz' % str(random.randint(100000,1000000)) + boundary = b"------hpiLO3t%dz" % str(random.randint(100000, 1000000)) parts = [ - b"""--%s\r\nContent-Disposition: form-data; name="fileType"\r\n\r\n""" % boundary, - b"""\r\n--%s\r\nContent-Disposition: form-data; name="fwimgfile"; filename="%s"\r\nContent-Type: application/octet-stream\r\n\r\n""" % (boundary, fsencode(filename)), + b"""--%s\r\nContent-Disposition: form-data; name="fileType"\r\n\r\n""" + % boundary, + b"""\r\n--%s\r\nContent-Disposition: form-data; name="fwimgfile"; filename="%s"\r\nContent-Type: application/octet-stream\r\n\r\n""" + % (boundary, fsencode(filename)), firmware, b"\r\n--%s--\r\n" % boundary, ] @@ -306,54 +374,67 @@ def _upload_file(self, filename, progress): sent = 0 fwlen = len(part) while sent < fwlen: - written = sock.write(part[sent:sent+self.BLOCK_SIZE]) + written = sock.write(part[sent : sent + self.BLOCK_SIZE]) if written is None: - plen = len(part[sent:sent+self.BLOCK_SIZE]) - raise IloCommunicationError("Unexpected EOF while sending %d bytes (%d of %d sent before)" % (plen, sent, fwlen)) + plen = len(part[sent : sent + self.BLOCK_SIZE]) + raise IloCommunicationError( + "Unexpected EOF while sending %d bytes (%d of %d sent before)" + % (plen, sent, fwlen) + ) sent += written if callable(progress): - progress("Sending request %d/%d bytes (%d%%)" % (sent, fwlen, 100.0*sent/fwlen)) + progress( + "Sending request %d/%d bytes (%d%%)" + % (sent, fwlen, 100.0 * sent / fwlen) + ) - data = '' + data = "" try: while True: d = sock.read() - data += d.decode('ascii') + data += d.decode("ascii") if not d: break - except socket.sslerror as exc: # Connection closed + except socket.sslerror as exc: # Connection closed if not data: - raise IloCommunicationError("Communication with %s:%d failed: %s" % (self.hostname, self.port, str(exc))) + raise IloCommunicationError( + "Communication with %s:%d failed: %s" + % (self.hostname, self.port, str(exc)) + ) self._debug(1, "Received %d bytes" % len(data)) self._debug(2, data) - if 'Set-Cookie:' not in data: + if "Set-Cookie:" not in data: # Seen on ilo3 with corrupt filesystem - body = re.search('(.*)', data, flags=re.DOTALL).group(1) - body = re.sub('<[^>]*>', '', body).strip() - body = re.sub('Return to last page', '', body).strip() - body = re.sub('\s+', ' ', body).strip() + body = re.search("(.*)", data, flags=re.DOTALL).group(1) + body = re.sub("<[^>]*>", "", body).strip() + body = re.sub("Return to last page", "", body).strip() + body = re.sub("\s+", " ", body).strip() raise IloError(body) - self.cookie = re.search('Set-Cookie: *(.*)', data).group(1) + self.cookie = re.search("Set-Cookie: *(.*)", data).group(1) self._debug(2, "Cookie: %s" % self.cookie) def _get_socket(self): """Set up a subprocess or an https connection and do an HTTP/raw socket request""" if self.read_response or self.save_request: + class FakeSocket(object): def __init__(self, rfile=None, wfile=None): - self.input = rfile and open(rfile, 'rb') or io.BytesIO() - self.output = wfile and open(wfile, 'ab') or io.BytesIO() + self.input = rfile and open(rfile, "rb") or io.BytesIO() + self.output = wfile and open(wfile, "ab") or io.BytesIO() self.read = self.input.read self.write = self.output.write data = self.input.read(4) self.input.seek(0) - self.protocol = data == b'HTTP' and ILO_HTTP or ILO_RAW + self.protocol = data == b"HTTP" and ILO_HTTP or ILO_RAW + def close(self): self.input.close() self.output.close() + shutdown = lambda *args: None + sock = FakeSocket(self.read_response, self.save_request) if self.read_response: self.protocol = sock.protocol @@ -362,9 +443,16 @@ def close(self): if self.protocol == ILO_LOCAL: self._debug(1, "Launching hponcfg") try: - sp = subprocess.Popen([self.hponcfg, '--input', '--xmlverbose'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sp = subprocess.Popen( + [self.hponcfg, "--input", "--xmlverbose"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) except OSError as exc: - raise IloCommunicationError("Cannot run %s: %s" % (self.hponcfg, str(exc))) + raise IloCommunicationError( + "Cannot run %s: %s" % (self.hponcfg, str(exc)) + ) sp.write = sp.stdin.write sp.read = sp.stdout.read return sp @@ -382,11 +470,16 @@ def close(self): except socket.timeout: if sock is not None: sock.close() - err = IloCommunicationError("Timeout connecting to %s port %d" % (self.hostname, self.port)) + err = IloCommunicationError( + "Timeout connecting to %s port %d" % (self.hostname, self.port) + ) except socket.error as exc: if sock is not None: sock.close() - err = IloCommunicationError("Error connecting to %s port %d: %s" % (self.hostname, self.port, str(exc))) + err = IloCommunicationError( + "Error connecting to %s port %d: %s" + % (self.hostname, self.port, str(exc)) + ) if err is not None: raise err @@ -401,10 +494,12 @@ def close(self): # which was dropped from the default cipher suite in # Python 2.7.10 and Python 3.4.4. Add it back here :( self.ssl_context.set_ciphers(ssl._DEFAULT_CIPHERS + ":RC4-SHA") - return self.ssl_context.wrap_socket( - sock, server_hostname=self.hostname) + return self.ssl_context.wrap_socket(sock, server_hostname=self.hostname) except ssl.SSLError as exc: - raise IloCommunicationError("Cannot establish ssl session with %s:%d: %s" % (self.hostname, self.port, str(exc))) + raise IloCommunicationError( + "Cannot establish ssl session with %s:%d: %s" + % (self.hostname, self.port, str(exc)) + ) def _communicate(self, xml, protocol, progress=None, save=True): sock = self._get_socket() @@ -412,9 +507,9 @@ def _communicate(self, xml, protocol, progress=None, save=True): protocol = sock.protocol msglen = len(self.XML_HEADER + xml) if protocol == ILO_HTTP: - extra_header = b'' + extra_header = b"" if self.cookie: - extra_header = b"\r\nCookie: %s" % self.cookie.encode('ascii') + extra_header = b"\r\nCookie: %s" % self.cookie.encode("ascii") http_header = self.HTTP_HEADER % (msglen, extra_header) msglen += len(http_header) self._debug(1, "Sending XML request, %d bytes" % msglen) @@ -428,18 +523,23 @@ def _communicate(self, xml, protocol, progress=None, save=True): # XML header and data need to arrive in 2 distinct packets if self.protocol != ILO_LOCAL: sock.write(self.XML_HEADER) - if b'$EMBED' in xml: - pre, name, post = re.compile(b'(.*)\$EMBED:(.*)\$(.*)', re.DOTALL).match(xml).groups() + if b"$EMBED" in xml: + pre, name, post = ( + re.compile(b"(.*)\$EMBED:(.*)\$(.*)", re.DOTALL).match(xml).groups() + ) sock.write(pre) sent = 0 fwlen = os.path.getsize(name) - with open(name, 'rb') as fd: + with open(name, "rb") as fd: fw = fd.read() while sent < fwlen: - written = sock.write(fw[sent:sent+self.BLOCK_SIZE]) + written = sock.write(fw[sent : sent + self.BLOCK_SIZE]) sent += written if callable(progress): - progress("Sending request %d/%d bytes (%d%%)" % (sent, fwlen, 100.0*sent/fwlen)) + progress( + "Sending request %d/%d bytes (%d%%)" + % (sent, fwlen, 100.0 * sent / fwlen) + ) sock.write(post.strip()) else: sock.write(xml) @@ -451,17 +551,17 @@ def _communicate(self, xml, protocol, progress=None, save=True): if self.protocol == ILO_LOCAL: # hponcfg doesn't return data until stdin is closed sock.stdin.close() - data = '' + data = "" try: while True: - d = sock.read().decode('ascii', 'iloxml_replace') + d = sock.read().decode("ascii", "iloxml_replace") data += d if not d: break - if callable(progress) and d.strip().endswith(''): - d = d[d.find('"): + d = d[d.find("')+1] + data = data[data.find("<") : data.rfind(">") + 1] if self.save_response and save: - with open(self.save_response, 'a') as fd: + with open(self.save_response, "a") as fd: fd.write(data) # Do we have HTTP? - header_ = '' - if protocol == ILO_HTTP and data.startswith('HTTP/1.1 200'): - header, data = data.split('\r\n\r\n', 1) + header_ = "" + if protocol == ILO_HTTP and data.startswith("HTTP/1.1 200"): + header, data = data.split("\r\n\r\n", 1) header_ = header - header = [x.split(':', 1) for x in header.split('\r\n')[1:]] + header = [x.split(":", 1) for x in header.split("\r\n")[1:]] header = dict([(x[0].lower(), x[1].strip()) for x in header]) - if header['transfer-encoding'] == 'chunked': - _data, data = data, '' + if header["transfer-encoding"] == "chunked": + _data, data = data, "" while _data: - clen, _data = _data.split('\r\n', 1) + clen, _data = _data.split("\r\n", 1) clen = int(clen, 16) if clen == 0: break data += _data[:clen] - _data = _data[clen+2:] + _data = _data[clen + 2 :] - elif data.startswith('HTTP/1.1 404'): + elif data.startswith("HTTP/1.1 404"): # We must be using iLO2 or older, they don't do HTTP for XML requests # This case is only triggered by the protocol detection header = None - elif not data.startswith('' in data: data = data.replace('', '') # Remove binary 01 in xml output. This bug was seen on a faulty PSU. - if '\x01' in data: - data = data.replace('\x01', '') - + if "\x01" in data: + data = data.replace("\x01", "") + # Quite a few unescaped quotation mark bugs keep appearing. Let's try # to fix up the XML by replacing the last occurrence of a quotation mark # *before* the position of the error. @@ -577,12 +692,12 @@ def _attempt_to_fix_broken_xml(self, data): return etree.fromstring(data) except etree.ParseError as e: position = e.position - x = position[0]-1 + x = position[0] - 1 y = position[1] lines = data.splitlines() y = lines[x].rfind('"', 0, y) - lines[x] = lines[x][:y] + '"' + lines[x][y+1:] - data = '\n'.join(lines) + lines[x] = lines[x][:y] + """ + lines[x][y + 1 :] + data = "\n".join(lines) # Couldn't fix it :( raise @@ -595,31 +710,34 @@ def _parse_message(self, data, include_inform=False): message = etree.fromstring(data) except etree.ParseError: message = self._attempt_to_fix_broken_xml(data) - if message.tag == 'RIBCL': + if message.tag == "RIBCL": for child in message: - if child.tag == 'INFORM': + if child.tag == "INFORM": if include_inform: # Filter useless message: - if 'should be updated' in child.text: + if "should be updated" in child.text: return None return child.text # RESPONSE with status 0 also adds no value # Maybe start adding to requests. TODO: check compatibility - elif child.tag == 'RESPONSE' and int(child.get('STATUS'), 16) == 0: - if child.get('MESSAGE') != 'No error': - warnings.warn(child.get('MESSAGE'), IloWarning) + elif child.tag == "RESPONSE" and int(child.get("STATUS"), 16) == 0: + if child.get("MESSAGE") != "No error": + warnings.warn(child.get("MESSAGE"), IloWarning) # These are interesting, something went wrong - elif child.tag == 'RESPONSE': - if 'syntax error' in child.get('MESSAGE') and not self.protocol: + elif child.tag == "RESPONSE": + if "syntax error" in child.get("MESSAGE") and not self.protocol: # This is triggered when doing protocol detection, ignore pass else: - status = int(child.get('STATUS'), 16) - message = child.get('MESSAGE') - if 'syntax error' in message: - message += '. You may have tried to use a feature this iLO version or firmware version does not support.' + status = int(child.get("STATUS"), 16) + message = child.get("MESSAGE") + if "syntax error" in message: + message += ". You may have tried to use a feature this iLO version or firmware version does not support." for subclass in IloError.known_subclasses: - if status in subclass.possible_codes or message in subclass.possible_messages: + if ( + status in subclass.possible_codes + or message in subclass.possible_messages + ): raise subclass(message, status) raise IloError(message, status) # And this type of message is the actual payload. @@ -631,7 +749,7 @@ def _parse_message(self, data, include_inform=False): def _element_children_to_dict(self, element): """Returns a dict with tag names of all child elements as keys and the - VALUE attributes as values""" + VALUE attributes as values""" retval = {} keys = [elt.tag.lower() for elt in element] if len(keys) != 1 and len(set(keys)) == 1: @@ -639,16 +757,25 @@ def _element_children_to_dict(self, element): retval = [] for elt in element: # There are some special tags - fname = '_parse_%s_%s' % (element.tag.lower(), elt.tag.lower()) + fname = "_parse_%s_%s" % (element.tag.lower(), elt.tag.lower()) if hasattr(self, fname): retval.update(getattr(self, fname)(elt)) continue - key, val, unit, description = elt.tag.lower(), elt.get('VALUE', elt.get('value', None)), elt.get('UNIT', None), elt.get('DESCRIPTION', None) + key, val, unit, description = ( + elt.tag.lower(), + elt.get("VALUE", elt.get("value", None)), + elt.get("UNIT", None), + elt.get("DESCRIPTION", None), + ) if val is None: # HP is not best friends with consistency. Sometimes there are # attributes, sometimes child tags and sometimes text nodes. Oh # well, deal with it :) - if element.tag.lower() == 'rimp' or elt.tag.lower() in self.xmldata_ectd.get(element.tag.lower(), []) or elt.tag.lower() == 'temps': + if ( + element.tag.lower() == "rimp" + or elt.tag.lower() in self.xmldata_ectd.get(element.tag.lower(), []) + or elt.tag.lower() == "temps" + ): val = self._element_children_to_dict(elt) elif elt.attrib and list(elt): val = self._element_to_dict(elt) @@ -685,17 +812,17 @@ def _element_to_dict(self, element): if list(element): fields = [] for child in element: - if child.tag == 'FIELD': + if child.tag == "FIELD": fields.append(self._element_to_dict(child)) if fields: - names = [x['name'] for x in fields] + names = [x["name"] for x in fields] if len(names) == len(set(names)): # Field names are unique, treat them like attributes for field in fields: - retval[field['name']] = field['value'] + retval[field["name"]] = field["value"] else: # Field names are not unique, such as the name "MAC" - retval['fields'] = fields + retval["fields"] = fields return retval def _element_to_list(self, element): @@ -703,18 +830,22 @@ def _element_to_list(self, element): if len(set(tagnames)) == 1: return [self._element_children_to_dict(x) for x in element] else: - return [(child.tag.lower(), self._element_to_dict(child)) for child in element] + return [ + (child.tag.lower(), self._element_to_dict(child)) for child in element + ] def _coerce(self, val): """Do some data type coercion: unquote, turn integers into integers and - Y/N into booleans""" + Y/N into booleans""" if isinstance(val, basestring): if val.startswith('"') and val.endswith('"'): val = val[1:-1] if val.isdigit(): val = int(val) else: - val = {'Y': True, 'N': False, 'true': True, 'false': False}.get(val, val) + val = {"Y": True, "N": False, "true": True, "false": False}.get( + val, val + ) return val def _raw(self, *tags): @@ -730,11 +861,15 @@ def _raw(self, *tags): fd.close() return ret - def _info_tag(self, infotype, tagname, returntags=None, attrib={}, process=lambda x: x): - root, inner = self._root_element(infotype, MODE='read') + def _info_tag( + self, infotype, tagname, returntags=None, attrib={}, process=lambda x: x + ): + root, inner = self._root_element(infotype, MODE="read") etree.SubElement(inner, tagname, **attrib) if self.delayed: - self._processors.append([self._process_info_tag, returntags or [tagname], process]) + self._processors.append( + [self._process_info_tag, returntags or [tagname], process] + ) return header, message = self._request(root) if self.save_request: @@ -755,16 +890,20 @@ def _process_info_tag(self, message, returntags, process): return process(self._element_to_dict(message)) raise IloError("Expected tag '%s' not found" % "' or '".join(returntags)) - def _control_tag(self, controltype, tagname, returntag=None, attrib={}, elements=[], text=None): - root, inner = self._root_element(controltype, MODE='write') + def _control_tag( + self, controltype, tagname, returntag=None, attrib={}, elements=[], text=None + ): + root, inner = self._root_element(controltype, MODE="write") inner = etree.SubElement(inner, tagname, **attrib) if text: inner.text = text for element in elements: inner.append(element) if self.delayed: - if tagname == 'CERTIFICATE_SIGNING_REQUEST': - self._processors.append([self._process_control_tag, returntag or tagname]) + if tagname == "CERTIFICATE_SIGNING_REQUEST": + self._processors.append( + [self._process_control_tag, returntag or tagname] + ) return header, message = self._request(root) return self._process_control_tag(message, returntag or tagname) @@ -785,18 +924,18 @@ def _process_control_tag(self, message, returntag): def call_delayed(self): """In delayed mode, calling a method on an iLO object will not cause an - immediate callout to the iLO. Instead, the method and parameters are - stored for future calls of this method. This method makes one - connection to the iLO and sends all commands as one XML document. - This speeds up applications that make many calls to the iLO by - removing seconds of overhead per call. - - The return value of call_delayed is a list of return values for - individual methods that don't return None. This means that there may - be fewer items returned than methods called as only `get_*` methods - return data + immediate callout to the iLO. Instead, the method and parameters are + stored for future calls of this method. This method makes one + connection to the iLO and sends all commands as one XML document. + This speeds up applications that make many calls to the iLO by + removing seconds of overhead per call. + + The return value of call_delayed is a list of return values for + individual methods that don't return None. This means that there may + be fewer items returned than methods called as only `get_*` methods + return data - Delayed calls only work on iLO 2 or newer""" + Delayed calls only work on iLO 2 or newer""" if not self._elements: raise ValueError("No commands scheduled") @@ -816,82 +955,123 @@ def call_delayed(self): def abort_dir_test(self): """Abort authentication directory test""" - return self._control_tag('DIR_INFO', 'ABORT_DIR_TEST') + return self._control_tag("DIR_INFO", "ABORT_DIR_TEST") def activate_license(self, key): """Activate an iLO advanced license""" - license = etree.Element('ACTIVATE', KEY=key) - return self._control_tag('RIB_INFO', 'LICENSE', elements=[license]) - - def add_federation_group(self, group_name, group_key, admin_priv=False, - remote_cons_priv=True, reset_server_priv=False, - virtual_media_priv=False, config_ilo_priv=True, login_priv=False): + license = etree.Element("ACTIVATE", KEY=key) + return self._control_tag("RIB_INFO", "LICENSE", elements=[license]) + + def add_federation_group( + self, + group_name, + group_key, + admin_priv=False, + remote_cons_priv=True, + reset_server_priv=False, + virtual_media_priv=False, + config_ilo_priv=True, + login_priv=False, + ): """Add a new federation group""" attrs = locals() elements = [] - for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: - val = ['No', 'Yes'][bool(attrs[attribute])] + for attribute in [x for x in attrs.keys() if x.endswith("_priv")]: + val = ["No", "Yes"][bool(attrs[attribute])] elements.append(etree.Element(attribute.upper(), VALUE=val)) - return self._control_tag('RIB_INFO', 'ADD_FEDERATION_GROUP', elements=elements, - attrib={'GROUP_NAME': group_name, 'GROUP_KEY': group_key}) + return self._control_tag( + "RIB_INFO", + "ADD_FEDERATION_GROUP", + elements=elements, + attrib={"GROUP_NAME": group_name, "GROUP_KEY": group_key}, + ) def add_sso_server(self, server=None, import_from=None, certificate=None): """Add an SSO server by name (only if SSO trust level is lowered) or by - importing a certificate from a server or directly""" + importing a certificate from a server or directly""" if [server, import_from, certificate].count(None) != 2: - raise ValueError("You must specify exactly one of server, import_from or certificate") + raise ValueError( + "You must specify exactly one of server, import_from or certificate" + ) if server: - return self._control_tag('SSO_INFO', 'SSO_SERVER', attrib={'NAME': server}) + return self._control_tag("SSO_INFO", "SSO_SERVER", attrib={"NAME": server}) if import_from: - return self._control_tag('SSO_INFO', 'SSO_SERVER', attrib={'IMPORT_FROM': import_from}) + return self._control_tag( + "SSO_INFO", "SSO_SERVER", attrib={"IMPORT_FROM": import_from} + ) if certificate: - return self._control_tag('SSO_INFO', 'IMPORT_CERTIFICATE', text=certificate) - - def add_user(self, user_login, user_name, password, admin_priv=False, - remote_cons_priv=True, reset_server_priv=False, - virtual_media_priv=False, config_ilo_priv=True): + return self._control_tag("SSO_INFO", "IMPORT_CERTIFICATE", text=certificate) + + def add_user( + self, + user_login, + user_name, + password, + admin_priv=False, + remote_cons_priv=True, + reset_server_priv=False, + virtual_media_priv=False, + config_ilo_priv=True, + ): """Add a new user to the iLO interface with the specified name, - password and permissions. Permission attributes should be boolean - values.""" + password and permissions. Permission attributes should be boolean + values.""" attrs = locals() elements = [] - for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: - val = ['No', 'Yes'][bool(attrs[attribute])] + for attribute in [x for x in attrs.keys() if x.endswith("_priv")]: + val = ["No", "Yes"][bool(attrs[attribute])] elements.append(etree.Element(attribute.upper(), VALUE=val)) - return self._control_tag('USER_INFO', 'ADD_USER', elements=elements, - attrib={'USER_LOGIN': user_login, 'USER_NAME': user_name, 'PASSWORD': DoNotEscapeMe(password)}) + return self._control_tag( + "USER_INFO", + "ADD_USER", + elements=elements, + attrib={ + "USER_LOGIN": user_login, + "USER_NAME": user_name, + "PASSWORD": DoNotEscapeMe(password), + }, + ) def ahs_clear_data(self): """Clears Active Health System information log""" - return self._control_tag('RIB_INFO', 'AHS_CLEAR_DATA') + return self._control_tag("RIB_INFO", "AHS_CLEAR_DATA") def cert_fqdn(self, use_fqdn): """Configure whether to use the fqdn or the short hostname for certificate requests""" - use_fqdn = str({True: 'Yes', False: 'No'}.get(use_fqdn, use_fqdn)) - return self._control_tag('RIB_INFO', 'CERT_FQDN', attrib={'VALUE': use_fqdn}) - - def certificate_signing_request(self, country=None, state=None, locality=None, organization=None, - organizational_unit=None, common_name=None): + use_fqdn = str({True: "Yes", False: "No"}.get(use_fqdn, use_fqdn)) + return self._control_tag("RIB_INFO", "CERT_FQDN", attrib={"VALUE": use_fqdn}) + + def certificate_signing_request( + self, + country=None, + state=None, + locality=None, + organization=None, + organizational_unit=None, + common_name=None, + ): """Get a certificate signing request from the iLO""" vars = locals() - del vars['self'] - vars = [('CSR_' + x.upper(), vars[x]) for x in vars if vars[x]] - elements = map(lambda x: etree.Element(x[0], attrib={'VALUE': str(x[1])}), vars) - return self._control_tag('RIB_INFO', 'CERTIFICATE_SIGNING_REQUEST', elements=elements) + del vars["self"] + vars = [("CSR_" + x.upper(), vars[x]) for x in vars if vars[x]] + elements = map(lambda x: etree.Element(x[0], attrib={"VALUE": str(x[1])}), vars) + return self._control_tag( + "RIB_INFO", "CERTIFICATE_SIGNING_REQUEST", elements=elements + ) def clear_ilo_event_log(self): """Clears the iLO event log""" - return self._control_tag('RIB_INFO', 'CLEAR_EVENTLOG') + return self._control_tag("RIB_INFO", "CLEAR_EVENTLOG") def clear_server_event_log(self): """Clears the server event log""" - return self._control_tag('SERVER_INFO', 'CLEAR_IML') + return self._control_tag("SERVER_INFO", "CLEAR_IML") def clear_server_power_on_time(self): """Clears the server power on time""" - return self._control_tag('SERVER_INFO', 'CLEAR_SERVER_POWER_ON_TIME') + return self._control_tag("SERVER_INFO", "CLEAR_SERVER_POWER_ON_TIME") def computer_lock_config(self, computer_lock=None, computer_lock_key=None): """Configure the computer lock settings""" @@ -899,133 +1079,149 @@ def computer_lock_config(self, computer_lock=None, computer_lock_key=None): computer_lock = "custom" if not computer_lock: raise ValueError("A value must be specified for computer_lock") - elements = [etree.Element('COMPUTER_LOCK', VALUE=computer_lock)] + elements = [etree.Element("COMPUTER_LOCK", VALUE=computer_lock)] if computer_lock_key: - elements.append(etree.Element('COMPUTER_LOCK_KEY', VALUE=computer_lock_key)) - return self._control_tag('RIB_INFO', 'COMPUTER_LOCK_CONFIG', elements=elements) + elements.append(etree.Element("COMPUTER_LOCK_KEY", VALUE=computer_lock_key)) + return self._control_tag("RIB_INFO", "COMPUTER_LOCK_CONFIG", elements=elements) def dc_registration_complete(self): """Complete the ERS registration of your device after calling - set_ers_direct_connect""" - return self._control_tag('RIB_INFO', 'DC_REGISTRATION_COMPLETE') + set_ers_direct_connect""" + return self._control_tag("RIB_INFO", "DC_REGISTRATION_COMPLETE") def delete_federation_group(self, group_name): """Delete the specified federation group membership""" - return self._control_tag('RIB_INFO', 'DELETE_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}) + return self._control_tag( + "RIB_INFO", "DELETE_FEDERATION_GROUP", attrib={"GROUP_NAME": group_name} + ) def delete_sso_server(self, index): """Delete an SSO server by index""" - return self._control_tag('SSO_INFO', 'DELETE_SERVER', - attrib={'INDEX': str(index)}) + return self._control_tag( + "SSO_INFO", "DELETE_SERVER", attrib={"INDEX": str(index)} + ) def delete_user(self, user_login): """Delete the specified user from the ilo""" - return self._control_tag('USER_INFO', 'DELETE_USER', attrib={'USER_LOGIN': user_login}) + return self._control_tag( + "USER_INFO", "DELETE_USER", attrib={"USER_LOGIN": user_login} + ) def deactivate_license(self): """Delete the license key from the iLO""" - element = etree.Element('DEACTIVATE') - return self._control_tag('RIB_INFO', 'LICENSE', elements=[element]) + element = etree.Element("DEACTIVATE") + return self._control_tag("RIB_INFO", "LICENSE", elements=[element]) def disable_ers(self): """Disable Insight Remote Support functionality and unregister the server""" - return self._control_tag('RIB_INFO', 'DISABLE_ERS') + return self._control_tag("RIB_INFO", "DISABLE_ERS") def eject_virtual_floppy(self): """Eject the virtual floppy""" - return self._control_tag('RIB_INFO', 'EJECT_VIRTUAL_FLOPPY') + return self._control_tag("RIB_INFO", "EJECT_VIRTUAL_FLOPPY") def eject_virtual_media(self, device="cdrom"): """Eject the virtual media attached to the specified device""" - return self._control_tag('RIB_INFO', 'EJECT_VIRTUAL_MEDIA', - attrib={"DEVICE": device.upper()}) + return self._control_tag( + "RIB_INFO", "EJECT_VIRTUAL_MEDIA", attrib={"DEVICE": device.upper()} + ) def ers_ahs_submit(self, message_id, bb_days): """Submity AHS data to the insight remote support server""" elements = [ - etree.Element('MESSAGE_ID', attrib={'VALUE': str(message_id)}), - etree.Element('BB_DAYS', attrib={'VALUE': str(bb_days)}), + etree.Element("MESSAGE_ID", attrib={"VALUE": str(message_id)}), + etree.Element("BB_DAYS", attrib={"VALUE": str(bb_days)}), ] - return self._control_tag('RIB_INFO', 'TRIGGER_BB_DATA', elements=elements) + return self._control_tag("RIB_INFO", "TRIGGER_BB_DATA", elements=elements) def fips_enable(self): """Enable FIPS standard to enforce AES/3DES encryption, can only be - reset with a call to factory_defaults. Resets Administrator password - and license key""" - return self._control_tag('RIB_INFO', 'FIPS_ENABLE') + reset with a call to factory_defaults. Resets Administrator password + and license key""" + return self._control_tag("RIB_INFO", "FIPS_ENABLE") def factory_defaults(self): """Reset the iLO to factory default settings""" - return self._control_tag('RIB_INFO', 'FACTORY_DEFAULTS') + return self._control_tag("RIB_INFO", "FACTORY_DEFAULTS") def force_format(self): """Forcefully format the iLO's internal NAND flash. Only use this when - the iLO is having severe problems and its self-test fails""" - return self._control_tag('RIB_INFO', 'FORCE_FORMAT', attrib={'VALUE': 'all'}) + the iLO is having severe problems and its self-test fails""" + return self._control_tag("RIB_INFO", "FORCE_FORMAT", attrib={"VALUE": "all"}) def get_ahs_status(self): """Get active health system logging status""" - return self._info_tag('RIB_INFO', 'GET_AHS_STATUS') + return self._info_tag("RIB_INFO", "GET_AHS_STATUS") def get_all_users(self): """Get a list of all loginnames""" + def process(data): if isinstance(data, dict): data = data.values() return [x for x in data if x] - return self._info_tag('USER_INFO', 'GET_ALL_USERS', process=process) + return self._info_tag("USER_INFO", "GET_ALL_USERS", process=process) def get_all_user_info(self): """Get basic and authorization info of all users""" + def process(data): if isinstance(data, dict): data = data.values() - return dict([(x['user_login'], x) for x in data]) - return self._info_tag('USER_INFO', 'GET_ALL_USER_INFO', process=process) + return dict([(x["user_login"], x) for x in data]) + + return self._info_tag("USER_INFO", "GET_ALL_USER_INFO", process=process) def get_asset_tag(self): """Gets the server asset tag""" # The absence of an asset tag is communicated in a warning and there # will be *NO* returntag, hence the AttributeError. try: - return self._info_tag('SERVER_INFO', 'GET_ASSET_TAG') + return self._info_tag("SERVER_INFO", "GET_ASSET_TAG") except AttributeError: - return {'asset_tag': None} + return {"asset_tag": None} def get_cert_subject_info(self): """Get ssl certificate subject information""" - return self._info_tag('RIB_INFO', 'GET_CERT_SUBJECT_INFO', 'CSR_CERT_SETTINGS') + return self._info_tag("RIB_INFO", "GET_CERT_SUBJECT_INFO", "CSR_CERT_SETTINGS") def get_critical_temp_remain_off(self): """Get whether the server will remain powered off after a critical temperature shutdown""" - return self._info_tag('SERVER_INFO', 'GET_CRITICAL_TEMP_REMAIN_OFF') + return self._info_tag("SERVER_INFO", "GET_CRITICAL_TEMP_REMAIN_OFF") def get_current_boot_mode(self): """Get the current boot mode (legaci or uefi)""" - return self._info_tag('SERVER_INFO', 'GET_CURRENT_BOOT_MODE', process=lambda data: data['boot_mode']) + return self._info_tag( + "SERVER_INFO", + "GET_CURRENT_BOOT_MODE", + process=lambda data: data["boot_mode"], + ) def get_diagport_settings(self): """Get the blade diagport settings""" - return self._info_tag('RACK_INFO', 'GET_DIAGPORT_SETTINGS') + return self._info_tag("RACK_INFO", "GET_DIAGPORT_SETTINGS") def get_dir_config(self): """Get directory authentication configuration""" - return self._info_tag('DIR_INFO', 'GET_DIR_CONFIG') + return self._info_tag("DIR_INFO", "GET_DIR_CONFIG") def get_dir_test_results(self): """Get the results of the authentication directory test""" + def process(data): for item in data: - data[item] = dict([(x[0], x[1]['value']) for x in data[item]]) + data[item] = dict([(x[0], x[1]["value"]) for x in data[item]]) return data - return self._info_tag('DIR_INFO', 'GET_DIR_TEST_RESULTS', process=process) + + return self._info_tag("DIR_INFO", "GET_DIR_TEST_RESULTS", process=process) def get_embedded_health(self): """Get server health information""" + def process(data): for category in data: - if category == 'health_at_a_glance': + if category == "health_at_a_glance": health = {} for key, val in data[category]: if key not in health: @@ -1035,41 +1231,47 @@ def process(data): data[category] = health continue elif isinstance(data[category], list) and data[category]: - for tag in ('label', 'location'): + for tag in ("label", "location"): if tag in data[category][0]: data[category] = dict([(x[tag], x) for x in data[category]]) break - elif data[category] in ['', []]: + elif data[category] in ["", []]: data[category] = None return data - return self._info_tag('SERVER_INFO', 'GET_EMBEDDED_HEALTH', 'GET_EMBEDDED_HEALTH_DATA', - process=process) + + return self._info_tag( + "SERVER_INFO", + "GET_EMBEDDED_HEALTH", + "GET_EMBEDDED_HEALTH_DATA", + process=process, + ) # Ok, special XML structures. Yay. def _parse_get_embedded_health_data_drives(self, element): ret = [] for bp in element: - if bp.tag != 'BACKPLANE': + if bp.tag != "BACKPLANE": raise IloError("Unexpected data returned: %s" % bp.tag) - backplane = obj = {'drive_bays': {}} + backplane = obj = {"drive_bays": {}} ret.append(backplane) for elt in bp: - if elt.tag == 'DRIVE_BAY': + if elt.tag == "DRIVE_BAY": obj = {} - backplane['drive_bays'][int(elt.get('VALUE'))] = obj + backplane["drive_bays"][int(elt.get("VALUE"))] = obj else: - obj[elt.tag.lower()] = elt.get('VALUE') - return {'drives_backplanes': ret} + obj[elt.tag.lower()] = elt.get("VALUE") + return {"drives_backplanes": ret} def _parse_get_embedded_health_data_memory(self, element): ret = {} for elt in element: - fname = '_parse_%s_%s' % (element.tag.lower(), elt.tag.lower()) + fname = "_parse_%s_%s" % (element.tag.lower(), elt.tag.lower()) if hasattr(self, fname): ret.update(getattr(self, fname)(elt)) continue ret[elt.tag.lower()] = self._element_children_to_dict(elt) return {element.tag.lower(): ret} + _parse_memory_memory_details_summary = _parse_get_embedded_health_data_memory def _parse_memory_memory_details(self, element): @@ -1082,37 +1284,44 @@ def _parse_memory_memory_details(self, element): return {element.tag.lower(): ret} def _parse_get_embedded_health_data_nic_information(self, element): - return {'nic_information': [self._element_children_to_dict(elt) for elt in element]} + return { + "nic_information": [self._element_children_to_dict(elt) for elt in element] + } + # Can you notice the misspelling?Yes, this is an actual bug in the HP firmware, seen in at least ilo3 1.70 - _parse_get_embedded_health_data_nic_infomation = _parse_get_embedded_health_data_nic_information + _parse_get_embedded_health_data_nic_infomation = ( + _parse_get_embedded_health_data_nic_information + ) def _parse_get_embedded_health_data_firmware_information(self, element): ret = {} for elt in element: data = self._element_children_to_dict(elt) - ret[data['firmware_name']] = data['firmware_version'] + ret[data["firmware_name"]] = data["firmware_version"] return {element.tag.lower(): ret} def _parse_get_embedded_health_data_storage(self, element): key = element.tag.lower() ret = {key: []} for ctrl in element: - if ctrl.tag == 'DISCOVERY_STATUS': - ret['%s_%s' % (key, ctrl.tag.lower())] = self._element_children_to_dict(ctrl)['status'] + if ctrl.tag == "DISCOVERY_STATUS": + ret["%s_%s" % (key, ctrl.tag.lower())] = self._element_children_to_dict( + ctrl + )["status"] continue data = {} for elt in ctrl: tag = elt.tag.lower() - if tag in ('drive_enclosure', 'logical_drive'): - tag += 's' + if tag in ("drive_enclosure", "logical_drive"): + tag += "s" if tag not in data: data[tag] = [] - if tag == 'drive_enclosures': + if tag == "drive_enclosures": data[tag].append(self._element_children_to_dict(elt)) else: data[tag].append(self._parse_logical_drive(elt)) else: - data[tag] = elt.get('VALUE') + data[tag] = elt.get("VALUE") ret[key].append(data) return ret @@ -1120,13 +1329,13 @@ def _parse_logical_drive(self, element): data = {} for elt in element: tag = elt.tag.lower() - if tag == 'physical_drive': - tag += 's' + if tag == "physical_drive": + tag += "s" if tag not in data: data[tag] = [] data[tag].append(self._element_children_to_dict(elt)) else: - data[tag] = elt.get('VALUE') + data[tag] = elt.get("VALUE") return data def _parse_get_embedded_health_data_power_supplies(self, element): @@ -1134,145 +1343,206 @@ def _parse_get_embedded_health_data_power_supplies(self, element): ret = {key: {}} for elt in element: data = self._element_children_to_dict(elt) - if 'label' in data: - ret[key][data['label']] = data + if "label" in data: + ret[key][data["label"]] = data else: ret[elt.tag.lower()] = data return ret def get_enclosure_ip_settings(self): """Get the enclosure bay static IP settings""" - return self._info_tag('RACK_INFO', 'GET_ENCLOSURE_IP_SETTINGS') + return self._info_tag("RACK_INFO", "GET_ENCLOSURE_IP_SETTINGS") def get_encrypt_settings(self): """Get the iLO encryption settings""" - return self._info_tag('RIB_INFO', 'GET_ENCRYPT_SETTINGS') + return self._info_tag("RIB_INFO", "GET_ENCRYPT_SETTINGS") def get_ers_settings(self): """Get the ERS Insight Remote Support settings""" - return self._info_tag('RIB_INFO', 'GET_ERS_SETTINGS') + return self._info_tag("RIB_INFO", "GET_ERS_SETTINGS") def get_federation_all_groups(self): """Get all federation group names""" + def process(data): if isinstance(data, dict): data = data.values() return data - return self._info_tag('RIB_INFO', 'GET_FEDERATION_ALL_GROUPS', process=process) + + return self._info_tag("RIB_INFO", "GET_FEDERATION_ALL_GROUPS", process=process) def get_federation_all_groups_info(self): """Get all federation group names and associated privileges""" + def process(data): if isinstance(data, dict): data = data.values() - data = [dict([(key, {'yes': True, 'no': False}.get(val['value'].lower(), val['value'])) for (key, val) in group]) for group in data] - return dict([(x['group_name'], x) for x in data]) - return self._info_tag('RIB_INFO', 'GET_FEDERATION_ALL_GROUPS_INFO', process=process) + data = [ + dict( + [ + ( + key, + {"yes": True, "no": False}.get( + val["value"].lower(), val["value"] + ), + ) + for (key, val) in group + ] + ) + for group in data + ] + return dict([(x["group_name"], x) for x in data]) + + return self._info_tag( + "RIB_INFO", "GET_FEDERATION_ALL_GROUPS_INFO", process=process + ) def get_federation_group(self, group_name): """Get privileges for a specific federation group""" + def process(data): - return dict([(key, {'yes': True, 'no': False}.get(val['value'].lower(), val['value'])) for (key, val) in data.values()[0]]) - return self._info_tag('RIB_INFO', 'GET_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}, process=process) + return dict( + [ + ( + key, + {"yes": True, "no": False}.get( + val["value"].lower(), val["value"] + ), + ) + for (key, val) in data.values()[0] + ] + ) + + return self._info_tag( + "RIB_INFO", + "GET_FEDERATION_GROUP", + attrib={"GROUP_NAME": group_name}, + process=process, + ) def get_federation_multicast(self): """Get the iLO federation multicast settings""" - return self._info_tag('RIB_INFO', 'GET_FEDERATION_MULTICAST') + return self._info_tag("RIB_INFO", "GET_FEDERATION_MULTICAST") def get_fips_status(self): """Is the FIPS-mandated AES/3DESencryption enforcement in place""" - return self._info_tag('RIB_INFO', 'GET_FIPS_STATUS') + return self._info_tag("RIB_INFO", "GET_FIPS_STATUS") def get_fw_version(self): """Get the iLO type and firmware version, use get_product_name to get the server model""" - return self._info_tag('RIB_INFO', 'GET_FW_VERSION') + return self._info_tag("RIB_INFO", "GET_FW_VERSION") def get_global_settings(self): """Get global iLO settings""" - return self._info_tag('RIB_INFO', 'GET_GLOBAL_SETTINGS') + return self._info_tag("RIB_INFO", "GET_GLOBAL_SETTINGS") def get_host_data(self, decoded_only=True): """Get SMBIOS records that describe the host. By default only the ones - where human readable information is available are returned. To get - all records pass :attr:`decoded_only=False` """ + where human readable information is available are returned. To get + all records pass :attr:`decoded_only=False`""" def process(data): if decoded_only: data = [x for x in data if len(x) > 2] return data - return self._info_tag('SERVER_INFO', 'GET_HOST_DATA', process=process) + + return self._info_tag("SERVER_INFO", "GET_HOST_DATA", process=process) def get_host_power_reg_info(self): """Get power regulator information""" - return self._control_tag('SERVER_INFO', 'GET_HOST_POWER_REG_INFO') + return self._control_tag("SERVER_INFO", "GET_HOST_POWER_REG_INFO") def get_host_power_saver_status(self): """Get the configuration of the ProLiant power regulator""" - return self._info_tag('SERVER_INFO', 'GET_HOST_POWER_SAVER_STATUS', 'GET_HOST_POWER_SAVER') + return self._info_tag( + "SERVER_INFO", "GET_HOST_POWER_SAVER_STATUS", "GET_HOST_POWER_SAVER" + ) def get_host_power_status(self): """Whether the server is powered on or not""" - return self._info_tag('SERVER_INFO', 'GET_HOST_POWER_STATUS', 'GET_HOST_POWER', - process=lambda data: data['host_power']) + return self._info_tag( + "SERVER_INFO", + "GET_HOST_POWER_STATUS", + "GET_HOST_POWER", + process=lambda data: data["host_power"], + ) def get_host_pwr_micro_ver(self): """Get the version of the power micro firmware""" - return self._info_tag('SERVER_INFO', 'GET_HOST_PWR_MICRO_VER', - process=lambda data: data['pwr_micro']['version']) + return self._info_tag( + "SERVER_INFO", + "GET_HOST_PWR_MICRO_VER", + process=lambda data: data["pwr_micro"]["version"], + ) def get_ilo_event_log(self): """Get the full iLO event log""" + def process(data): - if isinstance(data, dict) and 'event' in data: - return [data['event']] + if isinstance(data, dict) and "event" in data: + return [data["event"]] return data - return self._info_tag('RIB_INFO', 'GET_EVENT_LOG', 'EVENT_LOG', process=process) + + return self._info_tag("RIB_INFO", "GET_EVENT_LOG", "EVENT_LOG", process=process) def get_language(self): """Get the default language set""" - return self._info_tag('RIB_INFO', 'GET_LANGUAGE') + return self._info_tag("RIB_INFO", "GET_LANGUAGE") def get_all_languages(self): """Get the list of installed languages - broken because iLO returns invalid XML""" - return self._info_tag('RIB_INFO', 'GET_ALL_LANGUAGES') + return self._info_tag("RIB_INFO", "GET_ALL_LANGUAGES") def get_all_licenses(self): """Get a list of all license types and licenses""" + def process(data): if not isinstance(data, list): data = data.values() - return [dict([(x[0], x[1]['value']) for x in row]) for row in data] - return self._info_tag('RIB_INFO', 'GET_ALL_LICENSES', process=process) + return [dict([(x[0], x[1]["value"]) for x in row]) for row in data] + + return self._info_tag("RIB_INFO", "GET_ALL_LICENSES", process=process) def get_hotkey_config(self): """Retrieve hotkeys available for use in remote console sessions""" - return self._info_tag('RIB_INFO', 'GET_HOTKEY_CONFIG') + return self._info_tag("RIB_INFO", "GET_HOTKEY_CONFIG") def get_network_settings(self): """Get the iLO network settings""" - return self._info_tag('RIB_INFO', 'GET_NETWORK_SETTINGS') + return self._info_tag("RIB_INFO", "GET_NETWORK_SETTINGS") def get_oa_info(self): """Get information about the Onboard Administrator of the enclosing chassis""" - return self._info_tag('BLADESYSTEM_INFO', 'GET_OA_INFO') + return self._info_tag("BLADESYSTEM_INFO", "GET_OA_INFO") def get_one_time_boot(self): """Get the one time boot state of the host""" # Inconsistency between iLO 2 and 3, let's fix that def process(data): - if 'device' in data['boot_type']: - data['boot_type'] = data['boot_type']['device'] - return data['boot_type'].lower() - return self._info_tag('SERVER_INFO', 'GET_ONE_TIME_BOOT', ('ONE_TIME_BOOT', 'GET_ONE_TIME_BOOT'), process=process) + if "device" in data["boot_type"]: + data["boot_type"] = data["boot_type"]["device"] + return data["boot_type"].lower() + + return self._info_tag( + "SERVER_INFO", + "GET_ONE_TIME_BOOT", + ("ONE_TIME_BOOT", "GET_ONE_TIME_BOOT"), + process=process, + ) def get_pending_boot_mode(self): """Get the pending boot mode (legaci or uefi)""" - return self._info_tag('SERVER_INFO', 'GET_PENDING_BOOT_MODE', process=lambda data: data['boot_mode']) + return self._info_tag( + "SERVER_INFO", + "GET_PENDING_BOOT_MODE", + process=lambda data: data["boot_mode"], + ) def get_persistent_boot(self): """Get the boot order of the host. For uEFI hosts (gen9+), this returns - a list of tuples (name, description. For older host it returns a - list of names""" + a list of tuples (name, description. For older host it returns a + list of names""" + def process(data): if isinstance(data, dict): data = list(data.items()) @@ -1281,304 +1551,510 @@ def process(data): elif isinstance(data[0], tuple): return data return [x.lower() for x in data] - return self._info_tag('SERVER_INFO', 'GET_PERSISTENT_BOOT', ('PERSISTENT_BOOT', 'GET_PERSISTENT_BOOT'), process=process) + + return self._info_tag( + "SERVER_INFO", + "GET_PERSISTENT_BOOT", + ("PERSISTENT_BOOT", "GET_PERSISTENT_BOOT"), + process=process, + ) def get_pers_mouse_keyboard_enabled(self): """Returns whether persistent mouse and keyboard are enabled""" - return self._info_tag('SERVER_INFO', 'GET_PERS_MOUSE_KEYBOARD_ENABLED', process=lambda data: data['persmouse_enabled']) + return self._info_tag( + "SERVER_INFO", + "GET_PERS_MOUSE_KEYBOARD_ENABLED", + process=lambda data: data["persmouse_enabled"], + ) def get_power_cap(self): """Get the power cap setting""" - return self._info_tag('SERVER_INFO', 'GET_POWER_CAP', process=lambda data: data['power_cap']) + return self._info_tag( + "SERVER_INFO", "GET_POWER_CAP", process=lambda data: data["power_cap"] + ) def get_power_readings(self): """Get current, min, max and average power readings""" - return self._info_tag('SERVER_INFO', 'GET_POWER_READINGS') + return self._info_tag("SERVER_INFO", "GET_POWER_READINGS") def get_product_name(self): """Get the model name of the server, use get_fw_version to get the iLO model""" - return self._info_tag('SERVER_INFO', 'GET_PRODUCT_NAME', process=lambda data: data['product_name']) + return self._info_tag( + "SERVER_INFO", "GET_PRODUCT_NAME", process=lambda data: data["product_name"] + ) def get_pwreg(self): """Get the power and power alert threshold settings""" - return self._info_tag('SERVER_INFO', 'GET_PWREG') + return self._info_tag("SERVER_INFO", "GET_PWREG") def get_rack_settings(self): """Get the rack settings for an iLO""" - return self._info_tag('RACK_INFO', 'GET_RACK_SETTINGS') + return self._info_tag("RACK_INFO", "GET_RACK_SETTINGS") def get_sdcard_status(self): """Get whether an SD card is connected to the server""" - return self._info_tag('SERVER_INFO', 'GET_SDCARD_STATUS') + return self._info_tag("SERVER_INFO", "GET_SDCARD_STATUS") def get_security_msg(self): """Retrieve the security message that is displayed on the login screen""" - return self._info_tag('RIB_INFO', 'GET_SECURITY_MSG') + return self._info_tag("RIB_INFO", "GET_SECURITY_MSG") def get_server_auto_pwr(self): """Get the automatic power on delay setting""" - return self._info_tag('SERVER_INFO', 'GET_SERVER_AUTO_PWR', process=lambda data: data['server_auto_pwr']) + return self._info_tag( + "SERVER_INFO", + "GET_SERVER_AUTO_PWR", + process=lambda data: data["server_auto_pwr"], + ) def get_server_event_log(self): """Get the IML log of the server""" + def process(data): - if isinstance(data, dict) and 'event' in data: - return [data['event']] + if isinstance(data, dict) and "event" in data: + return [data["event"]] return data - return self._info_tag('SERVER_INFO', 'GET_EVENT_LOG', 'EVENT_LOG', process=process) + + return self._info_tag( + "SERVER_INFO", "GET_EVENT_LOG", "EVENT_LOG", process=process + ) def get_server_fqdn(self): """Get the fqdn of the server this iLO is managing""" - return self._info_tag('SERVER_INFO', 'GET_SERVER_FQDN', 'SERVER_FQDN', process=lambda fqdn: fqdn['value']) + return self._info_tag( + "SERVER_INFO", + "GET_SERVER_FQDN", + "SERVER_FQDN", + process=lambda fqdn: fqdn["value"], + ) def get_server_name(self): """Get the name of the server this iLO is managing""" - return self._info_tag('SERVER_INFO', 'GET_SERVER_NAME', 'SERVER_NAME', process=lambda name: name['value']) + return self._info_tag( + "SERVER_INFO", + "GET_SERVER_NAME", + "SERVER_NAME", + process=lambda name: name["value"], + ) def get_server_power_on_time(self): """How many minutes ago has the server been powered on""" - return self._info_tag('SERVER_INFO', 'GET_SERVER_POWER_ON_TIME', 'SERVER_POWER_ON_MINUTES', process=lambda data: int(data['value'])) + return self._info_tag( + "SERVER_INFO", + "GET_SERVER_POWER_ON_TIME", + "SERVER_POWER_ON_MINUTES", + process=lambda data: int(data["value"]), + ) def get_smh_fqdn(self): """Get the fqdn of the HP System Management Homepage""" - return self._info_tag('SERVER_INFO', 'GET_SMH_FQDN', 'SMH_FQDN', process=lambda fqdn: fqdn['value']) + return self._info_tag( + "SERVER_INFO", + "GET_SMH_FQDN", + "SMH_FQDN", + process=lambda fqdn: fqdn["value"], + ) def get_snmp_im_settings(self): """Where does the iLO send SNMP traps to and which traps does it send""" - return self._info_tag('RIB_INFO', 'GET_SNMP_IM_SETTINGS') + return self._info_tag("RIB_INFO", "GET_SNMP_IM_SETTINGS") def get_spatial(self): """Get location information""" - return self._info_tag('SERVER_INFO', 'GET_SPATIAL', 'SPATIAL') + return self._info_tag("SERVER_INFO", "GET_SPATIAL", "SPATIAL") def get_sso_settings(self): """Get the HP SIM Single Sign-On settings""" - return self._info_tag('SSO_INFO', 'GET_SSO_SETTINGS') + return self._info_tag("SSO_INFO", "GET_SSO_SETTINGS") def get_supported_boot_mode(self): - return self._info_tag('SERVER_INFO', 'GET_SUPPORTED_BOOT_MODE', process=lambda data: data['supported_boot_mode']) + return self._info_tag( + "SERVER_INFO", + "GET_SUPPORTED_BOOT_MODE", + process=lambda data: data["supported_boot_mode"], + ) def get_topology(self): """Get rack topology information""" - return self._info_tag('RACK_INFO', 'GET_TOPOLOGY') + return self._info_tag("RACK_INFO", "GET_TOPOLOGY") def get_tpm_status(self): """Get the status of the Trusted Platform Module""" - return self._info_tag('SERVER_INFO', 'GET_TPM_STATUS') + return self._info_tag("SERVER_INFO", "GET_TPM_STATUS") def get_twofactor_settings(self): """Get two-factor authentication settings""" - return self._info_tag('RIB_INFO', 'GET_TWOFACTOR_SETTINGS') + return self._info_tag("RIB_INFO", "GET_TWOFACTOR_SETTINGS") def get_uid_status(self): """Get the status of the UID light""" - return self._info_tag('SERVER_INFO', 'GET_UID_STATUS', process=lambda data: data['uid']) + return self._info_tag( + "SERVER_INFO", "GET_UID_STATUS", process=lambda data: data["uid"] + ) def get_user(self, user_login): """Get user info about a specific user""" - return self._info_tag('USER_INFO', 'GET_USER', attrib={'USER_LOGIN': user_login}) + return self._info_tag( + "USER_INFO", "GET_USER", attrib={"USER_LOGIN": user_login} + ) def get_vm_status(self, device="CDROM"): """Get the status of virtual media devices. Valid devices are FLOPPY and CDROM""" - return self._info_tag('RIB_INFO', 'GET_VM_STATUS', attrib={'DEVICE': device}) - - def hotkey_config(self, ctrl_t=None, ctrl_u=None, ctrl_v=None, ctrl_w=None, - ctrl_x=None, ctrl_y=None): + return self._info_tag("RIB_INFO", "GET_VM_STATUS", attrib={"DEVICE": device}) + + def hotkey_config( + self, + ctrl_t=None, + ctrl_u=None, + ctrl_v=None, + ctrl_w=None, + ctrl_x=None, + ctrl_y=None, + ): """Change remote console hotkeys""" vars = locals() - del vars['self'] - elements = [etree.Element(x.upper(), VALUE=vars[x]) for x in vars if vars[x] is not None] - return self._control_tag('RIB_INFO', 'HOTKEY_CONFIG', elements=elements) + del vars["self"] + elements = [ + etree.Element(x.upper(), VALUE=vars[x]) for x in vars if vars[x] is not None + ] + return self._control_tag("RIB_INFO", "HOTKEY_CONFIG", elements=elements) def import_certificate(self, certificate): """Import a signed SSL certificate""" - return self._control_tag('RIB_INFO', 'IMPORT_CERTIFICATE', text=certificate) + return self._control_tag("RIB_INFO", "IMPORT_CERTIFICATE", text=certificate) # Broken in iLO3 < 1.55 for Administrator def import_ssh_key(self, user_login, ssh_key): """Imports an SSH key for the specified user. The value of ssh_key - should be the content of an id_dsa.pub or id_rsa.pub file""" + should be the content of an id_dsa.pub or id_rsa.pub file""" # Basic sanity checking - if ' ' not in ssh_key: + if " " not in ssh_key: raise ValueError("Invalid SSH key") - algo, key = ssh_key.split(' ',2)[:2] - if algo not in ['ssh-dss', 'ssh-rsa']: + algo, key = ssh_key.split(" ", 2)[:2] + if algo not in ["ssh-dss", "ssh-rsa"]: raise ValueError("Invalid SSH key, only DSA and RSA keys are supported") try: - key.decode('base64') + key.decode("base64") except Exception: raise ValueError("Invalid SSH key") - key_ = "-----BEGIN SSH KEY-----\r\n%s\r\n%s %s\r\n-----END SSH KEY-----\r\n" % (algo, key, user_login) - return self._control_tag('RIB_INFO', 'IMPORT_SSH_KEY', text=key_) + key_ = "-----BEGIN SSH KEY-----\r\n%s\r\n%s %s\r\n-----END SSH KEY-----\r\n" % ( + algo, + key, + user_login, + ) + return self._control_tag("RIB_INFO", "IMPORT_SSH_KEY", text=key_) def delete_ssh_key(self, user_login): """Delete a users SSH key""" - return self._control_tag('USER_INFO', 'MOD_USER', attrib={'USER_LOGIN': user_login}, elements=[etree.Element('DEL_USERS_SSH_KEY')]) + return self._control_tag( + "USER_INFO", + "MOD_USER", + attrib={"USER_LOGIN": user_login}, + elements=[etree.Element("DEL_USERS_SSH_KEY")], + ) def insert_virtual_media(self, device, image_url): """Insert a virtual floppy or CDROM. Note that you will also need to - use :func:`set_vm_status` to connect the media""" - return self._control_tag('RIB_INFO', 'INSERT_VIRTUAL_MEDIA', attrib={'DEVICE': device.upper(), 'IMAGE_URL': image_url}) - - def mod_encrypt_settings(self, user_login, password, ilo_group_name, cert_name, enable_redundancy, - primary_server_address, primary_server_port, secondary_server_address=None, secondary_server_port=None): + use :func:`set_vm_status` to connect the media""" + return self._control_tag( + "RIB_INFO", + "INSERT_VIRTUAL_MEDIA", + attrib={"DEVICE": device.upper(), "IMAGE_URL": image_url}, + ) + + def mod_encrypt_settings( + self, + user_login, + password, + ilo_group_name, + cert_name, + enable_redundancy, + primary_server_address, + primary_server_port, + secondary_server_address=None, + secondary_server_port=None, + ): """Configure encryption settings""" vars = locals() - del vars['self'] + del vars["self"] elements = [] - for var in ['ilo_group_name', 'enable_redundancy']: + for var in ["ilo_group_name", "enable_redundancy"]: if vars[var] is not None: elements.append(etree.Element(var.upper(), VALUE=vars.pop(var))) for var in vars: if vars[var] is not None: - elements.append(etree.Element('ESKM_' + var.upper(), VALUE=vars[var])) - return self._control_tag('RIB_INFO', 'MOD_ENCRYPT_SETTINGS', elements=elements) - - def mod_federation_group(self, group_name, new_group_name=None, group_key=None, - admin_priv=None, remote_cons_priv=None, reset_server_priv=None, - virtual_media_priv=None, config_ilo_priv=None, login_priv=None): + elements.append(etree.Element("ESKM_" + var.upper(), VALUE=vars[var])) + return self._control_tag("RIB_INFO", "MOD_ENCRYPT_SETTINGS", elements=elements) + + def mod_federation_group( + self, + group_name, + new_group_name=None, + group_key=None, + admin_priv=None, + remote_cons_priv=None, + reset_server_priv=None, + virtual_media_priv=None, + config_ilo_priv=None, + login_priv=None, + ): """Set attributes for a federation group, only specified arguments will - be changed. All arguments except group_name, new_group_name and - group_key should be boolean""" + be changed. All arguments except group_name, new_group_name and + group_key should be boolean""" attrs = locals() elements = [] - if attrs['new_group_name'] is not None: - elements.append(etree.Element('GROUP_NAME', VALUE=attrs['new_group_name'])) - if attrs['group_key'] is not None: - elements.append(etree.Element('PASSWORD', VALUE=attrs['group_key'])) - for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: + if attrs["new_group_name"] is not None: + elements.append(etree.Element("GROUP_NAME", VALUE=attrs["new_group_name"])) + if attrs["group_key"] is not None: + elements.append(etree.Element("PASSWORD", VALUE=attrs["group_key"])) + for attribute in [x for x in attrs.keys() if x.endswith("_priv")]: if attrs[attribute] is not None: - val = ['No', 'Yes'][bool(attrs[attribute])] + val = ["No", "Yes"][bool(attrs[attribute])] elements.append(etree.Element(attribute.upper(), VALUE=val)) - return self._control_tag('RIB_INFO', 'MOD_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}, elements=elements) - - def mod_global_settings(self, ilo_funct_enabled=None, rbsu_post_ip=None, - # Access settings - f8_prompt_enabled=None, f8_login_required=None, lock_configuration=None, - serial_cli_status=None, serial_cli_speed=None, - http_port=None, https_port=None, ssh_port=None, ssh_status=None, - ipmi_dcmi_over_lan_enabled=None, ipmi_dcmi_over_lan_port=None, - remote_console_port_status=None, remote_console_port=None, remote_console_encryption=None, - rawvsp_port=None, vsp_software_flow_control=None, - terminal_services_port=None, - shared_console_enable=None, shared_console_port=None, remote_console_acquire=None, - telnet_enable=None, ssl_empty_records_enable=None, remote_console_status=None, - ribcl_status=None, virtual_media_status=None, webgui_status=None, webserver_status=None, - - # Security settings - min_password=None, enforce_aes=None, authentication_failure_logging=None, - authentication_failure_delay_secs=None, authentication_failures_before_delay=None, - ssl_v3_enable=None, session_timeout=None, - - # Monitoring & alerting - snmp_access_enabled=None, snmp_port=None, snmp_trap_port=None, - remote_syslog_enable=None, remote_syslog_server_address=None, remote_syslog_port=None, - alertmail_enable=None, alertmail_email_address=None, - alertmail_sender_domain=None, alertmail_smtp_server=None, alertmail_smtp_port=None, - alertmail_smtp_auth_enable=None, alertmail_smtp_auth_username=None, - alertmail_smtp_secure_enable=None, - - # Console capturing - vsp_log_enable=None, - interactive_console_replay_enable=None, console_capture_enable=None, - console_capture_boot_buffer_enable=None, console_capture_fault_buffer_enable=None, - console_capture_port=None, - capture_auto_export_enable=None, capture_auto_export_location=None, - capture_auto_export_username=None, capture_auto_export_password=None, - - # And the rest - remote_keyboard_model=None, virtual_kbmouse_connection=None, vmedia_disable=None, - virtual_media_port=None, key_up_key_down_enable=None, high_performance_mouse=None, - brownout_recovery=None, enhanced_cli_prompt_enable=None, tcp_keep_alive_enable=None, - propagate_time_to_host=None, passthrough_config=None): + return self._control_tag( + "RIB_INFO", + "MOD_FEDERATION_GROUP", + attrib={"GROUP_NAME": group_name}, + elements=elements, + ) + + def mod_global_settings( + self, + ilo_funct_enabled=None, + rbsu_post_ip=None, + # Access settings + f8_prompt_enabled=None, + f8_login_required=None, + lock_configuration=None, + serial_cli_status=None, + serial_cli_speed=None, + http_port=None, + https_port=None, + ssh_port=None, + ssh_status=None, + ipmi_dcmi_over_lan_enabled=None, + ipmi_dcmi_over_lan_port=None, + remote_console_port_status=None, + remote_console_port=None, + remote_console_encryption=None, + rawvsp_port=None, + vsp_software_flow_control=None, + terminal_services_port=None, + shared_console_enable=None, + shared_console_port=None, + remote_console_acquire=None, + telnet_enable=None, + ssl_empty_records_enable=None, + remote_console_status=None, + ribcl_status=None, + virtual_media_status=None, + webgui_status=None, + webserver_status=None, + # Security settings + min_password=None, + enforce_aes=None, + authentication_failure_logging=None, + authentication_failure_delay_secs=None, + authentication_failures_before_delay=None, + ssl_v3_enable=None, + session_timeout=None, + # Monitoring & alerting + snmp_access_enabled=None, + snmp_port=None, + snmp_trap_port=None, + remote_syslog_enable=None, + remote_syslog_server_address=None, + remote_syslog_port=None, + alertmail_enable=None, + alertmail_email_address=None, + alertmail_sender_domain=None, + alertmail_smtp_server=None, + alertmail_smtp_port=None, + alertmail_smtp_auth_enable=None, + alertmail_smtp_auth_username=None, + alertmail_smtp_secure_enable=None, + # Console capturing + vsp_log_enable=None, + interactive_console_replay_enable=None, + console_capture_enable=None, + console_capture_boot_buffer_enable=None, + console_capture_fault_buffer_enable=None, + console_capture_port=None, + capture_auto_export_enable=None, + capture_auto_export_location=None, + capture_auto_export_username=None, + capture_auto_export_password=None, + # And the rest + remote_keyboard_model=None, + virtual_kbmouse_connection=None, + vmedia_disable=None, + virtual_media_port=None, + key_up_key_down_enable=None, + high_performance_mouse=None, + brownout_recovery=None, + enhanced_cli_prompt_enable=None, + tcp_keep_alive_enable=None, + propagate_time_to_host=None, + passthrough_config=None, + ): """Modify iLO global settings, only values that are specified will be changed. Note that - many settings only work on certain iLO models and firmware versions""" + many settings only work on certain iLO models and firmware versions""" vars = dict(locals()) - del vars['self'] + del vars["self"] # even though a get_global_settings returns the actual speed we have to use # numerical values between 0 (unchanged) and 6 to represent speed serial_cli_speed_options = { - '9600': 1, - '19200': 2, - '38400': 3, - '57600': 4, - '115200': 5, + "9600": 1, + "19200": 2, + "38400": 3, + "57600": 4, + "115200": 5, } # same with serial_cli_status serial_cli_status_options = { - 'Disabled': 1, - 'Enabled-No Authentication': 2, - 'Enabled-Authentication Required': 3, + "Disabled": 1, + "Enabled-No Authentication": 2, + "Enabled-Authentication Required": 3, } # and authentication_failure_logging authentication_failure_logging_options = { - 'Disabled': 0, - 'Enabled-every failure': 1, - 'Enabled-every 2nd failure': 2, - 'Enabled-every 3rd failure': 3, - 'Enabled-every 5th failure': 5, + "Disabled": 0, + "Enabled-every failure": 1, + "Enabled-every 2nd failure": 2, + "Enabled-every 3rd failure": 3, + "Enabled-every 5th failure": 5, } vars_mappings = { "serial_cli_speed": serial_cli_speed_options, "serial_cli_status": serial_cli_status_options, - "authentication_failure_logging": authentication_failure_logging_options + "authentication_failure_logging": authentication_failure_logging_options, } for var_name, var_mappings in vars_mappings.items(): if vars.get(var_name, None) is not None: var_value = str(vars.get(var_name)) - vars[var_name] = str(var_mappings.get(var_value,var_value)) - - dont_map = ['authentication_failure_logging', 'authentication_failures_before_delay', 'serial_cli_speed', - 'min_password', 'session_timeout', "serial_cli_status"] - elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) - for x in vars if vars[x] is not None and x not in dont_map] + \ - [etree.Element(x.upper(), VALUE=str(vars[x])) - for x in vars if vars[x] is not None and x in dont_map] - return self._control_tag('RIB_INFO', 'MOD_GLOBAL_SETTINGS', elements=elements) - - def mod_network_settings(self, enable_nic=None, reg_ddns_server=None, - ping_gateway=None, dhcp_domain_name=None, speed_autoselect=None, - nic_speed=None, full_duplex=None, dhcp_enable=None, - ip_address=None, subnet_mask=None, gateway_ip_address=None, - dns_name=None, domain_name=None, dhcp_gateway=None, - dhcp_dns_server=None, dhcp_wins_server=None, dhcp_static_route=None, - reg_wins_server=None, prim_dns_server=None, sec_dns_server=None, - ter_dns_server=None, prim_wins_server=None, sec_wins_server=None, - static_route_1=None, static_route_2=None, static_route_3=None, - dhcp_sntp_settings=None, sntp_server1=None, sntp_server2=None, - timezone=None, enclosure_ip_enable=None, web_agent_ip_address=None, - shared_network_port=None, vlan_enabled=None, vlan_id=None, - shared_network_port_vlan=None, shared_network_port_vlan_id=None, ipv6_address=None, - ipv6_static_route_1=None, ipv6_static_route_2=None, ipv6_static_route_3=None, - ipv6_prim_dns_server=None, ipv6_sec_dns_server=None, ipv6_ter_dns_server=None, - ipv6_default_gateway=None, ipv6_preferred_protocol=None, ipv6_addr_autocfg=None, - ipv6_reg_ddns_server=None, dhcpv6_dns_server=None, dhcpv6_rapid_commit=None, - dhcpv6_stateful_enable=None, dhcpv6_stateless_enable=None, dhcpv6_sntp_settings=None, - dhcpv6_domain_name=None, ilo_nic_auto_select=None, ilo_nic_auto_snp_scan=None, - ilo_nic_auto_delay=None, ilo_nic_fail_over=None, gratuitous_arp=None, - ilo_nic_fail_over_delay=None, snp_port=None): + vars[var_name] = str(var_mappings.get(var_value, var_value)) + + dont_map = [ + "authentication_failure_logging", + "authentication_failures_before_delay", + "serial_cli_speed", + "min_password", + "session_timeout", + "serial_cli_status", + ] + elements = [ + etree.Element( + x.upper(), VALUE=str({True: "Yes", False: "No"}.get(vars[x], vars[x])) + ) + for x in vars + if vars[x] is not None and x not in dont_map + ] + [ + etree.Element(x.upper(), VALUE=str(vars[x])) + for x in vars + if vars[x] is not None and x in dont_map + ] + return self._control_tag("RIB_INFO", "MOD_GLOBAL_SETTINGS", elements=elements) + + def mod_network_settings( + self, + enable_nic=None, + reg_ddns_server=None, + ping_gateway=None, + dhcp_domain_name=None, + speed_autoselect=None, + nic_speed=None, + full_duplex=None, + dhcp_enable=None, + ip_address=None, + subnet_mask=None, + gateway_ip_address=None, + dns_name=None, + domain_name=None, + dhcp_gateway=None, + dhcp_dns_server=None, + dhcp_wins_server=None, + dhcp_static_route=None, + reg_wins_server=None, + prim_dns_server=None, + sec_dns_server=None, + ter_dns_server=None, + prim_wins_server=None, + sec_wins_server=None, + static_route_1=None, + static_route_2=None, + static_route_3=None, + dhcp_sntp_settings=None, + sntp_server1=None, + sntp_server2=None, + timezone=None, + enclosure_ip_enable=None, + web_agent_ip_address=None, + shared_network_port=None, + vlan_enabled=None, + vlan_id=None, + shared_network_port_vlan=None, + shared_network_port_vlan_id=None, + ipv6_address=None, + ipv6_static_route_1=None, + ipv6_static_route_2=None, + ipv6_static_route_3=None, + ipv6_prim_dns_server=None, + ipv6_sec_dns_server=None, + ipv6_ter_dns_server=None, + ipv6_default_gateway=None, + ipv6_preferred_protocol=None, + ipv6_addr_autocfg=None, + ipv6_reg_ddns_server=None, + dhcpv6_dns_server=None, + dhcpv6_rapid_commit=None, + dhcpv6_stateful_enable=None, + dhcpv6_stateless_enable=None, + dhcpv6_sntp_settings=None, + dhcpv6_domain_name=None, + ilo_nic_auto_select=None, + ilo_nic_auto_snp_scan=None, + ilo_nic_auto_delay=None, + ilo_nic_fail_over=None, + gratuitous_arp=None, + ilo_nic_fail_over_delay=None, + snp_port=None, + ): """Configure the network settings for the iLO card. The static route arguments require - dicts as arguments. The necessary keys in these dicts are dest, - gateway and mask all in dotted-quad form""" + dicts as arguments. The necessary keys in these dicts are dest, + gateway and mask all in dotted-quad form""" vars = dict(locals()) - del vars['self'] + del vars["self"] # For the ipv4 route elements, {'dest': XXX, 'gateway': XXX} # ipv6 routes are ipv6_dest, prefixlen, ipv6_gateway # IPv6 addresses may specify prefixlength as /64 (default 64) - dont_map = ['prefixlen', 'ilo_nic_auto_snp_scan', 'ilo_nic_auto_delay', 'ilo_nic_fail_over_delay', 'snp_port', 'vlan_id'] - elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) - for x in vars if vars[x] is not None and 'static_route_' not in x and x not in dont_map] + \ - [etree.Element(x.upper(), VALUE=str(vars[x])) - for x in vars if vars[x] is not None and 'static_route_' not in x and x in dont_map] + dont_map = [ + "prefixlen", + "ilo_nic_auto_snp_scan", + "ilo_nic_auto_delay", + "ilo_nic_fail_over_delay", + "snp_port", + "vlan_id", + ] + elements = [ + etree.Element( + x.upper(), VALUE=str({True: "Yes", False: "No"}.get(vars[x], vars[x])) + ) + for x in vars + if vars[x] is not None and "static_route_" not in x and x not in dont_map + ] + [ + etree.Element(x.upper(), VALUE=str(vars[x])) + for x in vars + if vars[x] is not None and "static_route_" not in x and x in dont_map + ] for key in vars: - if 'static_route_' not in key or not vars[key]: + if "static_route_" not in key or not vars[key]: continue val = vars[key] # Uppercase all keys @@ -1587,458 +2063,696 @@ def mod_network_settings(self, enable_nic=None, reg_ddns_server=None, elements.append(etree.Element(key.upper(), **val)) for element in elements: - if element.tag == 'IPV6_ADDRESS': - addr = element.attrib['VALUE'] - if '/' in addr: - addr, plen = addr.rsplit('/', 1) - element.attrib.update({'VALUE': addr, 'PREFIXLEN': plen}) - if 'PREFIXLEN' not in element.attrib: - element.attrib['PREFIXLEN'] = '64' + if element.tag == "IPV6_ADDRESS": + addr = element.attrib["VALUE"] + if "/" in addr: + addr, plen = addr.rsplit("/", 1) + element.attrib.update({"VALUE": addr, "PREFIXLEN": plen}) + if "PREFIXLEN" not in element.attrib: + element.attrib["PREFIXLEN"] = "64" if "IPV6_STATIC_ROUTE_" in element.tag: - plen = element.attrib['PREFIXLEN'] + plen = element.attrib["PREFIXLEN"] if not isinstance(plen, basestring): - element.attrib['PREFIXLEN'] = str(plen) - return self._control_tag('RIB_INFO', 'MOD_NETWORK_SETTINGS', elements=elements) - mod_network_settings.requires_dict = ['static_route_1', 'static_route_2', 'static_route_3', - 'ipv6_static_route_1', 'ipv6_static_route_2', 'ipv6_static_route_3'] - - def mod_dir_config(self, dir_authentication_enabled=None, - dir_local_user_acct=None,dir_server_address=None, - dir_server_port=None,dir_object_dn=None,dir_object_password=None, - dir_user_context_1=None,dir_user_context_2=None, - dir_user_context_3=None,dir_user_context_4=None, - dir_user_context_5=None,dir_user_context_6=None, - dir_user_context_7=None,dir_user_context_8=None, - dir_user_context_9=None,dir_user_context_10=None, - dir_user_context_11=None,dir_user_context_12=None, - dir_user_context_13=None,dir_user_context_14=None, - dir_user_context_15=None,dir_enable_grp_acct=None, - dir_kerberos_enabled=None,dir_kerberos_realm=None, - dir_kerberos_kdc_address=None,dir_kerberos_kdc_port=None, - dir_kerberos_keytab=None, - dir_generic_ldap_enabled=None, - dir_grpacct1_name=None,dir_grpacct1_sid=None, - dir_grpacct1_priv=None,dir_grpacct2_name=None, - dir_grpacct2_sid=None,dir_grpacct2_priv=None, - dir_grpacct3_name=None,dir_grpacct3_sid=None, - dir_grpacct3_priv=None,dir_grpacct4_name=None, - dir_grpacct4_sid=None,dir_grpacct4_priv=None, - dir_grpacct5_name=None,dir_grpacct5_sid=None, - dir_grpacct5_priv=None,dir_grpacct6_name=None, - dir_grpacct6_sid=None,dir_grpacct6_priv=None): + element.attrib["PREFIXLEN"] = str(plen) + return self._control_tag("RIB_INFO", "MOD_NETWORK_SETTINGS", elements=elements) + + mod_network_settings.requires_dict = [ + "static_route_1", + "static_route_2", + "static_route_3", + "ipv6_static_route_1", + "ipv6_static_route_2", + "ipv6_static_route_3", + ] + + def mod_dir_config( + self, + dir_authentication_enabled=None, + dir_local_user_acct=None, + dir_server_address=None, + dir_server_port=None, + dir_object_dn=None, + dir_object_password=None, + dir_user_context_1=None, + dir_user_context_2=None, + dir_user_context_3=None, + dir_user_context_4=None, + dir_user_context_5=None, + dir_user_context_6=None, + dir_user_context_7=None, + dir_user_context_8=None, + dir_user_context_9=None, + dir_user_context_10=None, + dir_user_context_11=None, + dir_user_context_12=None, + dir_user_context_13=None, + dir_user_context_14=None, + dir_user_context_15=None, + dir_enable_grp_acct=None, + dir_kerberos_enabled=None, + dir_kerberos_realm=None, + dir_kerberos_kdc_address=None, + dir_kerberos_kdc_port=None, + dir_kerberos_keytab=None, + dir_generic_ldap_enabled=None, + dir_grpacct1_name=None, + dir_grpacct1_sid=None, + dir_grpacct1_priv=None, + dir_grpacct2_name=None, + dir_grpacct2_sid=None, + dir_grpacct2_priv=None, + dir_grpacct3_name=None, + dir_grpacct3_sid=None, + dir_grpacct3_priv=None, + dir_grpacct4_name=None, + dir_grpacct4_sid=None, + dir_grpacct4_priv=None, + dir_grpacct5_name=None, + dir_grpacct5_sid=None, + dir_grpacct5_priv=None, + dir_grpacct6_name=None, + dir_grpacct6_sid=None, + dir_grpacct6_priv=None, + ): """Modify iLO directory configuration, only values that are specified - will be changed.""" + will be changed.""" vars = dict(locals()) - del vars['self'] + del vars["self"] # The _priv thing is a comma-separated list of numbers, but other # functions use names, and the iLO ssh interface shows different names. # Support them all. privmap = { - 'login': 1, - 'rc': 2, - 'remote_cons': 2, - 'vm': 3, - 'virtual_media': 3, - 'power': 4, - 'reset_server': 4, - 'config': 5, - 'config_ilo': 5, - 'admin': 6, + "login": 1, + "rc": 2, + "remote_cons": 2, + "vm": 3, + "virtual_media": 3, + "power": 4, + "reset_server": 4, + "config": 5, + "config_ilo": 5, + "admin": 6, } # create special case for element with text inside if dir_kerberos_keytab: - keytab_el = etree.Element('DIR_KERBEROS_KEYTAB') + keytab_el = etree.Element("DIR_KERBEROS_KEYTAB") keytab_el.text = dir_kerberos_keytab - del vars['dir_kerberos_keytab'] + del vars["dir_kerberos_keytab"] elements = [] for key, val in vars.items(): if val is None: continue - if key.endswith('_priv'): + if key.endswith("_priv"): if isinstance(val, basestring): - val = val.replace('oemhp_', '').replace('_priv', '').split(',') - if not hasattr(val, '__iter__'): + val = val.replace("oemhp_", "").replace("_priv", "").split(",") + if not hasattr(val, "__iter__"): val = [val] - val = ','.join([str(privmap.get(x,x)) for x in val]) + val = ",".join([str(privmap.get(x, x)) for x in val]) else: - val = str({True: 'Yes', False: 'No'}.get(val, val)) + val = str({True: "Yes", False: "No"}.get(val, val)) elements.append(etree.Element(key.upper(), VALUE=val)) if dir_kerberos_keytab: elements.append(keytab_el) - return self._control_tag('DIR_INFO','MOD_DIR_CONFIG',elements=elements) - - - def mod_snmp_im_settings(self, snmp_access=None, web_agent_ip_address=None, - snmp_address_1=None, snmp_address_1_rocommunity=None, snmp_address_1_trapcommunity=None, - snmp_address_2=None, snmp_address_2_rocommunity=None, snmp_address_2_trapcommunity=None, - snmp_address_3=None, snmp_address_3_rocommunity=None, snmp_address_3_trapcommunity=None, - snmp_port=None, snmp_trap_port=None, snmp_v3_engine_id=None, snmp_passthrough_status=None, - trap_source_identifier=None, os_traps=None, rib_traps=None, cold_start_trap_broadcast=None, - snmp_v1_traps=None, cim_security_mask=None, snmp_sys_location=None, snmp_sys_contact=None, - agentless_management_enable=None, snmp_system_role=None, snmp_system_role_detail=None, - snmp_user_profile_1=None, snmp_user_profile_2=None, snmp_user_profile_3=None): + return self._control_tag("DIR_INFO", "MOD_DIR_CONFIG", elements=elements) + + def mod_snmp_im_settings( + self, + snmp_access=None, + web_agent_ip_address=None, + snmp_address_1=None, + snmp_address_1_rocommunity=None, + snmp_address_1_trapcommunity=None, + snmp_address_2=None, + snmp_address_2_rocommunity=None, + snmp_address_2_trapcommunity=None, + snmp_address_3=None, + snmp_address_3_rocommunity=None, + snmp_address_3_trapcommunity=None, + snmp_port=None, + snmp_trap_port=None, + snmp_v3_engine_id=None, + snmp_passthrough_status=None, + trap_source_identifier=None, + os_traps=None, + rib_traps=None, + cold_start_trap_broadcast=None, + snmp_v1_traps=None, + cim_security_mask=None, + snmp_sys_location=None, + snmp_sys_contact=None, + agentless_management_enable=None, + snmp_system_role=None, + snmp_system_role_detail=None, + snmp_user_profile_1=None, + snmp_user_profile_2=None, + snmp_user_profile_3=None, + ): """Configure the SNMP and Insight Manager integration settings. The - trapcommunity settings must be dicts with keys value (the name of - the community) and version (1 or 2c)""" + trapcommunity settings must be dicts with keys value (the name of + the community) and version (1 or 2c)""" vars = dict(locals()) - del vars['self'] - elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) - for x in vars if vars[x] is not None and 'trapcommunity' not in x and 'snmp_user_profile' not in x] + del vars["self"] + elements = [ + etree.Element( + x.upper(), VALUE=str({True: "Yes", False: "No"}.get(vars[x], vars[x])) + ) + for x in vars + if vars[x] is not None + and "trapcommunity" not in x + and "snmp_user_profile" not in x + ] for key in vars: - if 'trapcommunity' in key and vars[key]: + if "trapcommunity" in key and vars[key]: val = vars[key] for key_ in val.keys(): val[key_.upper()] = str(val.pop(key_)) elements.append(etree.Element(key.upper(), **val)) - elif 'snmp_user_profile' in key and vars[key]: - elt = etree.Element(key[:-2].upper(), {'INDEX': key[-1]}) + elif "snmp_user_profile" in key and vars[key]: + elt = etree.Element(key[:-2].upper(), {"INDEX": key[-1]}) for key, val in vars[key].items(): etree.SubElement(elt, key.upper(), VALUE=str(val)) elements.append(elt) - return self._control_tag('RIB_INFO', 'MOD_SNMP_IM_SETTINGS', elements=elements) - mod_snmp_im_settings.requires_dict = ['snmp_user_profile_1', 'snmp_user_profile_2', 'snmp_user_profile_3', - 'snmp_address_1_trapcommunity', 'snmp_address_2_trapcommunity', 'snmp_address_3_trapcommunity'] - - def mod_sso_settings(self, trust_mode=None, user_remote_cons_priv=None, - user_reset_server_priv=None, user_virtual_media_priv=None, - user_config_ilo_priv=None, user_admin_priv=None, - operator_login_priv=None, operator_remote_cons_priv=None, - operator_reset_server_priv=None, operator_virtual_media_priv=None, - operator_config_ilo_priv=None, operator_admin_priv=None, - administrator_login_priv=None, administrator_remote_cons_priv=None, - administrator_reset_server_priv=None, administrator_virtual_media_priv=None, - administrator_config_ilo_priv=None, administrator_admin_priv=None): + return self._control_tag("RIB_INFO", "MOD_SNMP_IM_SETTINGS", elements=elements) + + mod_snmp_im_settings.requires_dict = [ + "snmp_user_profile_1", + "snmp_user_profile_2", + "snmp_user_profile_3", + "snmp_address_1_trapcommunity", + "snmp_address_2_trapcommunity", + "snmp_address_3_trapcommunity", + ] + + def mod_sso_settings( + self, + trust_mode=None, + user_remote_cons_priv=None, + user_reset_server_priv=None, + user_virtual_media_priv=None, + user_config_ilo_priv=None, + user_admin_priv=None, + operator_login_priv=None, + operator_remote_cons_priv=None, + operator_reset_server_priv=None, + operator_virtual_media_priv=None, + operator_config_ilo_priv=None, + operator_admin_priv=None, + administrator_login_priv=None, + administrator_remote_cons_priv=None, + administrator_reset_server_priv=None, + administrator_virtual_media_priv=None, + administrator_config_ilo_priv=None, + administrator_admin_priv=None, + ): vars = dict(locals()) - del vars['self'] - del vars['trust_mode'] + del vars["self"] + del vars["trust_mode"] elements = [] if trust_mode is not None: - elements.append(etree.Element('TRUST_MODE', attrib={'VALUE': trust_mode})) - vars = [(x.upper().split('_', 1), {True: 'Yes', False: 'No'}.get(vars[x], vars[x])) for x in vars if vars[x]] - elements += [etree.Element(x[0][0] + '_ROLE', attrib={x[0][1]: x[1]}) for x in vars] - return self._control_tag('SSO_INFO', 'MOD_SSO_SETTINGS', elements=elements) - - def mod_twofactor_settings(self, auth_twofactor_enable=None, cert_revocation_check=None, cert_owner_san=None, cert_owner_subject=None): + elements.append(etree.Element("TRUST_MODE", attrib={"VALUE": trust_mode})) + vars = [ + (x.upper().split("_", 1), {True: "Yes", False: "No"}.get(vars[x], vars[x])) + for x in vars + if vars[x] + ] + elements += [ + etree.Element(x[0][0] + "_ROLE", attrib={x[0][1]: x[1]}) for x in vars + ] + return self._control_tag("SSO_INFO", "MOD_SSO_SETTINGS", elements=elements) + + def mod_twofactor_settings( + self, + auth_twofactor_enable=None, + cert_revocation_check=None, + cert_owner_san=None, + cert_owner_subject=None, + ): """Modify the twofactor authentication settings""" elements = [] if auth_twofactor_enable is not None: - elements.append(etree.Element('AUTH_TWOFACTOR_ENABLE', VALUE=['No', 'Yes'][bool(auth_twofactor_enable)])) + elements.append( + etree.Element( + "AUTH_TWOFACTOR_ENABLE", + VALUE=["No", "Yes"][bool(auth_twofactor_enable)], + ) + ) if cert_revocation_check is not None: - elements.append(etree.Element('CERT_REVOCATION_CHECK', VALUE=['No', 'Yes'][bool(cert_revocation_check)])) + elements.append( + etree.Element( + "CERT_REVOCATION_CHECK", + VALUE=["No", "Yes"][bool(cert_revocation_check)], + ) + ) if cert_owner_san: - elements.append(etree.Element('CERT_OWNER_SAN')) + elements.append(etree.Element("CERT_OWNER_SAN")) if cert_owner_subject: - elements.append(etree.Element('CERT_OWNER_SUBJECT')) - return self._control_tag('RIB_INFO', 'MOD_TWOFACTOR_SETTINGS', elements=elements) - - - def mod_user(self, user_login, user_name=None, password=None, - admin_priv=None, remote_cons_priv=None, reset_server_priv=None, - virtual_media_priv=None, config_ilo_priv=None): + elements.append(etree.Element("CERT_OWNER_SUBJECT")) + return self._control_tag( + "RIB_INFO", "MOD_TWOFACTOR_SETTINGS", elements=elements + ) + + def mod_user( + self, + user_login, + user_name=None, + password=None, + admin_priv=None, + remote_cons_priv=None, + reset_server_priv=None, + virtual_media_priv=None, + config_ilo_priv=None, + ): """Set attributes for a user, only specified arguments will be changed. - All arguments except user_name and password should be boolean""" + All arguments except user_name and password should be boolean""" attrs = locals() elements = [] - if attrs['user_name'] is not None: - elements.append(etree.Element('USER_NAME', VALUE=attrs['user_name'])) - if attrs['password'] is not None: - elements.append(etree.Element('PASSWORD', VALUE=DoNotEscapeMe(attrs['password']))) - for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: + if attrs["user_name"] is not None: + elements.append(etree.Element("USER_NAME", VALUE=attrs["user_name"])) + if attrs["password"] is not None: + elements.append( + etree.Element("PASSWORD", VALUE=DoNotEscapeMe(attrs["password"])) + ) + for attribute in [x for x in attrs.keys() if x.endswith("_priv")]: if attrs[attribute] is not None: - val = ['No', 'Yes'][bool(attrs[attribute])] + val = ["No", "Yes"][bool(attrs[attribute])] elements.append(etree.Element(attribute.upper(), VALUE=val)) - return self._control_tag('USER_INFO', 'MOD_USER', attrib={'USER_LOGIN': user_login}, elements=elements) + return self._control_tag( + "USER_INFO", + "MOD_USER", + attrib={"USER_LOGIN": user_login}, + elements=elements, + ) def press_pwr_btn(self): """Press the power button""" - return self._control_tag('SERVER_INFO', 'PRESS_PWR_BTN') + return self._control_tag("SERVER_INFO", "PRESS_PWR_BTN") def profile_apply(self, desc_name, action): """Apply a deployment profile""" elements = [ - etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name}), - etree.Element('PROFILE_OPTIONS', attrib={'VALUE': 'none'}), # Currently unused - etree.Element('PROFILE_ACTION', attrib={'VALUE': action}), + etree.Element("PROFILE_DESC_NAME", attrib={"VALUE": desc_name}), + etree.Element( + "PROFILE_OPTIONS", attrib={"VALUE": "none"} + ), # Currently unused + etree.Element("PROFILE_ACTION", attrib={"VALUE": action}), ] - return self._control_tag('RIB_INFO', 'PROFILE_APPLY', elements=elements) + return self._control_tag("RIB_INFO", "PROFILE_APPLY", elements=elements) def profile_apply_get_results(self): """Retrieve the results of the last profile_apply""" - return self._info_tag('RIB_INFO', 'PROFILE_APPLY_GET_RESULTS') + return self._info_tag("RIB_INFO", "PROFILE_APPLY_GET_RESULTS") def profile_delete(self, desc_name): """Delet the specified deployment profile""" - return self._control_tag('RIB_INFO', 'PROFILE_DELETE', elements=[etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name})]) - - def profile_desc_download(self, desc_name, name, description, blob_namespace='perm', blob_name=None, url=None): + return self._control_tag( + "RIB_INFO", + "PROFILE_DELETE", + elements=[etree.Element("PROFILE_DESC_NAME", attrib={"VALUE": desc_name})], + ) + + def profile_desc_download( + self, + desc_name, + name, + description, + blob_namespace="perm", + blob_name=None, + url=None, + ): """Make the iLO download a blob and create a deployment profile""" elements = [ - etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name}), - etree.Element('PROFILE_NAME', attrib={'VALUE': name}), - etree.Element('PROFILE_DESCRIPTION', attrib={'VALUE': description}), - etree.Element('PROFILE_SCHEMA', attrib={'VALUE': 'intelligentprovisioning.1.0.0'}), + etree.Element("PROFILE_DESC_NAME", attrib={"VALUE": desc_name}), + etree.Element("PROFILE_NAME", attrib={"VALUE": name}), + etree.Element("PROFILE_DESCRIPTION", attrib={"VALUE": description}), + etree.Element( + "PROFILE_SCHEMA", attrib={"VALUE": "intelligentprovisioning.1.0.0"} + ), ] if blob_namespace: - elements.append(etree.Element('BLOB_NAMESPACE', attrib={'VALUE': blob_namespace})) + elements.append( + etree.Element("BLOB_NAMESPACE", attrib={"VALUE": blob_namespace}) + ) if blob_name: - elements.append(etree.Element('BLOB_NAME', attrib={'VALUE': blob_name})) + elements.append(etree.Element("BLOB_NAME", attrib={"VALUE": blob_name})) else: - elements.append(etree.Element('BLOB_NAME', attrib={'VALUE': desc_name})) + elements.append(etree.Element("BLOB_NAME", attrib={"VALUE": desc_name})) if url: - elements.append(etree.Element('PROFILE_URL', attrib={'VALUE': url})) - return self._control_tag('RIB_INFO', 'PROFILE_DESC_DOWNLOAD', elements=elements) + elements.append(etree.Element("PROFILE_URL", attrib={"VALUE": url})) + return self._control_tag("RIB_INFO", "PROFILE_DESC_DOWNLOAD", elements=elements) def profile_list(self): """List all profile descriptors""" + def process(data): if isinstance(data, dict): return data.values() return data - return self._info_tag('RIB_INFO', 'PROFILE_LIST', 'PROFILE_DESC_LIST', process=process) + + return self._info_tag( + "RIB_INFO", "PROFILE_LIST", "PROFILE_DESC_LIST", process=process + ) def hold_pwr_btn(self, toggle=None): """Press and hold the power button""" attrib = {} if toggle is not None: - attrib['TOGGLE'] = ['No', 'Yes'][bool(toggle)] - return self._control_tag('SERVER_INFO', 'HOLD_PWR_BTN', attrib=attrib) + attrib["TOGGLE"] = ["No", "Yes"][bool(toggle)] + return self._control_tag("SERVER_INFO", "HOLD_PWR_BTN", attrib=attrib) def cold_boot_server(self): """Force a cold boot of the server""" - return self._control_tag('SERVER_INFO', 'COLD_BOOT_SERVER') + return self._control_tag("SERVER_INFO", "COLD_BOOT_SERVER") def warm_boot_server(self): """Force a warm boot of the server""" - return self._control_tag('SERVER_INFO', 'WARM_BOOT_SERVER') + return self._control_tag("SERVER_INFO", "WARM_BOOT_SERVER") def reset_rib(self): """Reset the iLO/RILOE board""" - return self._control_tag('RIB_INFO', 'RESET_RIB') + return self._control_tag("RIB_INFO", "RESET_RIB") def reset_server(self): """Power cycle the server""" - return self._control_tag('SERVER_INFO', 'RESET_SERVER') + return self._control_tag("SERVER_INFO", "RESET_SERVER") def send_snmp_test_trap(self): """Send an SNMP test trap to the configured alert destinations""" - return self._control_tag('RIB_INFO', 'SEND_SNMP_TEST_TRAP') + return self._control_tag("RIB_INFO", "SEND_SNMP_TEST_TRAP") def set_ahs_status(self, status): """Enable or disable AHS logging""" - status = {True: 'enable', False: 'disable'}[status] - return self._control_tag('RIB_INFO', 'SET_AHS_STATUS', attrib={'VALUE': status}) + status = {True: "enable", False: "disable"}[status] + return self._control_tag("RIB_INFO", "SET_AHS_STATUS", attrib={"VALUE": status}) def set_asset_tag(self, asset_tag): """Set the server asset tag""" - return self._control_tag('SERVER_INFO', 'SET_ASSET_TAG', attrib={'VALUE': asset_tag}) + return self._control_tag( + "SERVER_INFO", "SET_ASSET_TAG", attrib={"VALUE": asset_tag} + ) def set_critical_temp_remain_off(self, value): """Set whether the server will remain off after a critical temperature shutdown""" - status = {True: 'Yes', False: 'No'}[value] - return self._control_tag('SERVER_INFO', 'SET_CRITICAL_TEMP_REMAIN_OFF', attrib={'VALUE': value}) - - def set_ers_direct_connect(self, user_id, password, proxy_url=None, - proxy_port=None, proxy_username=None, proxy_password=None): + status = {True: "Yes", False: "No"}[value] + return self._control_tag( + "SERVER_INFO", "SET_CRITICAL_TEMP_REMAIN_OFF", attrib={"VALUE": value} + ) + + def set_ers_direct_connect( + self, + user_id, + password, + proxy_url=None, + proxy_port=None, + proxy_username=None, + proxy_password=None, + ): """Register your iLO with HP Insigt Online using Direct Connect. Note - that you must also call dc_registration_complete""" + that you must also call dc_registration_complete""" elements = [ - etree.Element('ERS_HPP_USER_ID', attrib={'VALUE': str(user_id)}), - etree.Element('ERS_HPP_PASSWORD', attrib={'VALUE': str(password)}), + etree.Element("ERS_HPP_USER_ID", attrib={"VALUE": str(user_id)}), + etree.Element("ERS_HPP_PASSWORD", attrib={"VALUE": str(password)}), ] for key, value in locals().items(): - if key.startswith('proxy_') and value is not None: - elements.append(etree.Element('ERS_WEB_' + key, attrib={'VALUE': str(value)})) - return self._control_tag('RIB_INFO', 'SET_ERS_DIRECT_CONNECT', elements=elements) + if key.startswith("proxy_") and value is not None: + elements.append( + etree.Element("ERS_WEB_" + key, attrib={"VALUE": str(value)}) + ) + return self._control_tag( + "RIB_INFO", "SET_ERS_DIRECT_CONNECT", elements=elements + ) def set_ers_irs_connect(self, ers_destination_url, ers_destination_port): """Connect to an Insight Remote Support server""" elements = [ - etree.Element('ERS_DESTINATION_URL', attrib={'VALUE': str(ers_destination_url)}), - etree.Element('ERS_DESTINATION_PORT', attrib={'VALUE': str(ers_destination_port)}), + etree.Element( + "ERS_DESTINATION_URL", attrib={"VALUE": str(ers_destination_url)} + ), + etree.Element( + "ERS_DESTINATION_PORT", attrib={"VALUE": str(ers_destination_port)} + ), ] - return self._control_tag('RIB_INFO', 'SET_ERS_IRS_CONNECT', elements=elements) + return self._control_tag("RIB_INFO", "SET_ERS_IRS_CONNECT", elements=elements) - def set_ers_web_proxy(self, proxy_url, proxy_port, proxy_username=None, - proxy_password=None): + def set_ers_web_proxy( + self, proxy_url, proxy_port, proxy_username=None, proxy_password=None + ): """Register your iLO with HP Insigt Online using Direct Connect. Note - that you must also call dc_registration_complete""" + that you must also call dc_registration_complete""" elements = [] for key, value in locals().items(): - if key.startswith('proxy_') and value is not None: - elements.append(etree.Element('ERS_WEB_' + key, attrib={'VALUE': str(value)})) - return self._control_tag('RIB_INFO', 'SET_ERS_WEB_PROXY', elements=elements) - - def set_federation_multicast(self, multicast_federation_enabled=True, multicast_discovery_enabled=True, - multicast_announcement_interval=600, ipv6_multicast_scope="Site", multicast_ttl=5): + if key.startswith("proxy_") and value is not None: + elements.append( + etree.Element("ERS_WEB_" + key, attrib={"VALUE": str(value)}) + ) + return self._control_tag("RIB_INFO", "SET_ERS_WEB_PROXY", elements=elements) + + def set_federation_multicast( + self, + multicast_federation_enabled=True, + multicast_discovery_enabled=True, + multicast_announcement_interval=600, + ipv6_multicast_scope="Site", + multicast_ttl=5, + ): """Set the Federation multicast configuration""" - multicast_federation_enabled = {True: 'Yes', False: 'No'}[multicast_federation_enabled] - multicast_discovery_enabled = {True: 'Yes', False: 'No'}[multicast_discovery_enabled] + multicast_federation_enabled = {True: "Yes", False: "No"}[ + multicast_federation_enabled + ] + multicast_discovery_enabled = {True: "Yes", False: "No"}[ + multicast_discovery_enabled + ] elements = [ - etree.Element('MULTICAST_FEDERATION_ENABLED', attrib={'VALUE': multicast_federation_enabled}), - etree.Element('MULTICAST_DISCOVERY_ENABLED', attrib={'VALUE': multicast_discovery_enabled}), - etree.Element('MULTICAST_ANNOUNCEMENT_INTERVAL', attrib={'VALUE': str(multicast_announcement_interval)}), - etree.Element('IPV6_MULTICAST_SCOPE', attrib={'VALUE': str(ipv6_multicast_scope)}), - etree.Element('MULTICAST_TTL', attrib={'VALUE': str(multicast_ttl)}), + etree.Element( + "MULTICAST_FEDERATION_ENABLED", + attrib={"VALUE": multicast_federation_enabled}, + ), + etree.Element( + "MULTICAST_DISCOVERY_ENABLED", + attrib={"VALUE": multicast_discovery_enabled}, + ), + etree.Element( + "MULTICAST_ANNOUNCEMENT_INTERVAL", + attrib={"VALUE": str(multicast_announcement_interval)}, + ), + etree.Element( + "IPV6_MULTICAST_SCOPE", attrib={"VALUE": str(ipv6_multicast_scope)} + ), + etree.Element("MULTICAST_TTL", attrib={"VALUE": str(multicast_ttl)}), ] - return self._control_tag('RIB_INFO', 'SET_FEDERATION_MULTICAST', elements=elements) - + return self._control_tag( + "RIB_INFO", "SET_FEDERATION_MULTICAST", elements=elements + ) def set_language(self, lang_id): """Set the default language. Only EN, JA and ZH are supported""" - return self._control_tag('RIB_INFO', 'SET_LANGUAGE', attrib={'LANG_ID': lang_id}) + return self._control_tag( + "RIB_INFO", "SET_LANGUAGE", attrib={"LANG_ID": lang_id} + ) def set_host_power(self, host_power=True): """Turn host power on or off""" - power = ['No', 'Yes'][bool(host_power)] - return self._control_tag('SERVER_INFO', 'SET_HOST_POWER', attrib={'HOST_POWER': power}) + power = ["No", "Yes"][bool(host_power)] + return self._control_tag( + "SERVER_INFO", "SET_HOST_POWER", attrib={"HOST_POWER": power} + ) def set_host_power_saver(self, host_power_saver): """Set the configuration of the ProLiant power regulator""" - mapping = {'off': 1, 'min': 2, 'auto': 3, 'max': 4} - host_power_saver = str(mapping.get(str(host_power_saver).lower(), host_power_saver)) - return self._control_tag('SERVER_INFO', 'SET_HOST_POWER_SAVER', attrib={'HOST_POWER_SAVER': host_power_saver}) + mapping = {"off": 1, "min": 2, "auto": 3, "max": 4} + host_power_saver = str( + mapping.get(str(host_power_saver).lower(), host_power_saver) + ) + return self._control_tag( + "SERVER_INFO", + "SET_HOST_POWER_SAVER", + attrib={"HOST_POWER_SAVER": host_power_saver}, + ) def set_one_time_boot(self, device): """Set one time boot device, device should be one of normal, floppy, - cdrom, hdd, usb, rbsu or network. Ilo 4 also supports EMB-MENU - (Displays the default boot menu), EMB-ACU (Boots into ACU), - EMB-HPSUM-AUTO (Boots HPSUM in automatic update mode), EMB-DIAGS - (Launches Insight Diagnostics for Linux in interactive mode) and - RBSU (Boots into the system RBSU)""" - if not device.lower().startswith('boot'): + cdrom, hdd, usb, rbsu or network. Ilo 4 also supports EMB-MENU + (Displays the default boot menu), EMB-ACU (Boots into ACU), + EMB-HPSUM-AUTO (Boots HPSUM in automatic update mode), EMB-DIAGS + (Launches Insight Diagnostics for Linux in interactive mode) and + RBSU (Boots into the system RBSU)""" + if not device.lower().startswith("boot"): device = device.upper() - return self._control_tag('SERVER_INFO', 'SET_ONE_TIME_BOOT', attrib={'VALUE': device}) + return self._control_tag( + "SERVER_INFO", "SET_ONE_TIME_BOOT", attrib={"VALUE": device} + ) def set_pending_boot_mode(self, boot_mode): """Set the boot mode for the next boot to UEFI or legacy""" - return self._control_tag('SERVER_INFO', 'SET_PENDING_BOOT_MODE', attrib={'VALUE': boot_mode.upper()}) + return self._control_tag( + "SERVER_INFO", "SET_PENDING_BOOT_MODE", attrib={"VALUE": boot_mode.upper()} + ) def set_persistent_boot(self, devices): """Set persistent boot order, devices should be comma-separated""" elements = [] if isinstance(devices, basestring): - devices = devices.split(',') + devices = devices.split(",") for device in devices: - if not device.lower().startswith('boot'): + if not device.lower().startswith("boot"): device = device.upper() - elements.append(etree.Element('DEVICE', VALUE=device)) - return self._control_tag('SERVER_INFO', 'SET_PERSISTENT_BOOT', elements=elements) + elements.append(etree.Element("DEVICE", VALUE=device)) + return self._control_tag( + "SERVER_INFO", "SET_PERSISTENT_BOOT", elements=elements + ) def set_pers_mouse_keyboard_enabled(self, enabled): """Enable/disable persistent mouse and keyboard""" - enabled = {True: 'Yes', False: 'No'}.get(enabled,enabled) - return self._control_tag('SERVER_INFO', 'SET_PERS_MOUSE_KEYBOARD_ENABLED', attrib={'VALUE': enabled}) + enabled = {True: "Yes", False: "No"}.get(enabled, enabled) + return self._control_tag( + "SERVER_INFO", "SET_PERS_MOUSE_KEYBOARD_ENABLED", attrib={"VALUE": enabled} + ) def set_pwreg(self, type, threshold=None, duration=None): """Set the power alert threshold""" - elements = [etree.Element('PWRALERT', TYPE=type)] + elements = [etree.Element("PWRALERT", TYPE=type)] if type.lower() != "disabled": - elements.append(etree.Element('PWRALERT_SETTINGS', THRESHOLD=str(threshold), DURATION=str(duration))) - return self._control_tag('SERVER_INFO', 'SET_PWREG', elements=elements) + elements.append( + etree.Element( + "PWRALERT_SETTINGS", + THRESHOLD=str(threshold), + DURATION=str(duration), + ) + ) + return self._control_tag("SERVER_INFO", "SET_PWREG", elements=elements) def set_power_cap(self, power_cap): """Set the power cap feature to a specific value""" - return self._control_tag('SERVER_INFO', 'SET_POWER_CAP', attrib={'POWER_CAP': str(power_cap)}) + return self._control_tag( + "SERVER_INFO", "SET_POWER_CAP", attrib={"POWER_CAP": str(power_cap)} + ) - def set_security_msg(self, security_msg, security_msg_text=''): + def set_security_msg(self, security_msg, security_msg_text=""): """Enables/disables the security message on the iLO login screen and sets its value""" - enabled = str({True: 'Yes', False: 'No'}.get(security_msg, security_msg)) - text = etree.Element('SECURITY_MSG_TEXT') + enabled = str({True: "Yes", False: "No"}.get(security_msg, security_msg)) + text = etree.Element("SECURITY_MSG_TEXT") text.append(CDATA(security_msg_text)) - elements = (etree.Element('SECURITY_MSG', VALUE=enabled), text) - return self._control_tag('RIB_INFO', 'SET_SECURITY_MSG', elements=elements) + elements = (etree.Element("SECURITY_MSG", VALUE=enabled), text) + return self._control_tag("RIB_INFO", "SET_SECURITY_MSG", elements=elements) def set_server_auto_pwr(self, setting): """Set the automatic power on delay setting. Valid settings are False, - True (for minimum delay), 15, 30, 45 60 (for that amount of delay) - or random (for a random delay of up to 60 seconds.)""" - setting = str({True: 'Yes', False: 'No'}.get(setting, setting)) - return self._control_tag('SERVER_INFO', 'SERVER_AUTO_PWR', attrib={'VALUE': setting}) + True (for minimum delay), 15, 30, 45 60 (for that amount of delay) + or random (for a random delay of up to 60 seconds.)""" + setting = str({True: "Yes", False: "No"}.get(setting, setting)) + return self._control_tag( + "SERVER_INFO", "SERVER_AUTO_PWR", attrib={"VALUE": setting} + ) def set_server_fqdn(self, fqdn): """Set the fqdn of the server""" - return self._control_tag('SERVER_INFO', 'SERVER_FQDN', attrib={"VALUE": fqdn}) + return self._control_tag("SERVER_INFO", "SERVER_FQDN", attrib={"VALUE": fqdn}) def set_server_name(self, name): """Set the name of the server""" try: - return self._control_tag('SERVER_INFO', 'SERVER_NAME', attrib={"VALUE": name}) + return self._control_tag( + "SERVER_INFO", "SERVER_NAME", attrib={"VALUE": name} + ) except IloError: # In their infinite wisdom, HP decided that only this tag should use value # instead of VALUE. And only for certain hardware/firmware combinations. # slowclap.mp3 - return self._control_tag('SERVER_INFO', 'SERVER_NAME', attrib={"value": name}) + return self._control_tag( + "SERVER_INFO", "SERVER_NAME", attrib={"value": name} + ) def set_vf_status(self, boot_option="boot_once", write_protect=True): """Set the parameters of the RILOE virtual floppy specified virtual media. Valid boot options are boot_once, boot_always, no_boot, connect and disconnect.""" - write_protect = ['NO', 'YES'][bool(write_protect)] + write_protect = ["NO", "YES"][bool(write_protect)] elements = [ - etree.Element('VF_BOOT_OPTION', value=boot_option.upper()), - etree.Element('VF_WRITE_PROTECT', value=write_protect), + etree.Element("VF_BOOT_OPTION", value=boot_option.upper()), + etree.Element("VF_WRITE_PROTECT", value=write_protect), ] - return self._control_tag('RIB_INFO', 'SET_VF_STATUS', elements=elements) + return self._control_tag("RIB_INFO", "SET_VF_STATUS", elements=elements) - def set_vm_status(self, device="cdrom", boot_option="boot_once", write_protect=True): + def set_vm_status( + self, device="cdrom", boot_option="boot_once", write_protect=True + ): """Set the parameters of the specified virtual media. Valid boot - options are boot_once, boot_always, no_boot, connect and disconnect. - Valid devices are floppy and cdrom""" + options are boot_once, boot_always, no_boot, connect and disconnect. + Valid devices are floppy and cdrom""" - write_protect = ['NO', 'YES'][bool(write_protect)] + write_protect = ["NO", "YES"][bool(write_protect)] elements = [ - etree.Element('VM_BOOT_OPTION', value=boot_option.upper()), - etree.Element('VM_WRITE_PROTECT', value=write_protect), + etree.Element("VM_BOOT_OPTION", value=boot_option.upper()), + etree.Element("VM_WRITE_PROTECT", value=write_protect), ] - return self._control_tag('RIB_INFO', 'SET_VM_STATUS', attrib={'DEVICE': device.upper()}, - elements=elements) - - def start_dir_test(self, dir_admin_distinguished_name, dir_admin_password, test_user_name, test_user_password): + return self._control_tag( + "RIB_INFO", + "SET_VM_STATUS", + attrib={"DEVICE": device.upper()}, + elements=elements, + ) + + def start_dir_test( + self, + dir_admin_distinguished_name, + dir_admin_password, + test_user_name, + test_user_password, + ): """Test directory authentication with the specified credentials""" vars = locals() - del vars['self'] + del vars["self"] elements = [etree.Element(x, VALUE=vars[x]) for x in vars] - return self._control_tag('DIR_INFO', 'START_DIR_TEST', elements=elements) + return self._control_tag("DIR_INFO", "START_DIR_TEST", elements=elements) def trigger_bb_data(self, message_id, days): """Initiate AHS data submission to IRS. The submitted data will include - the specified message ID and number of days of data""" - return self._control_tag('RIB_INFO', 'TRIGGER_BB_DATA', attrib={'MESSAGE_ID': message_id, 'BB_DAYS': days}) + the specified message ID and number of days of data""" + return self._control_tag( + "RIB_INFO", + "TRIGGER_BB_DATA", + attrib={"MESSAGE_ID": message_id, "BB_DAYS": days}, + ) def trigger_l2_collection(self, message_id): """Initiate an L2 data collection submission to the Insight Remote Support server.""" - element = etree.Element('MESSAGE_ID', attrib={'value': str(message_id)}) - return self._control_tag('RIB_INFO', 'TRIGGER_L2_COLLECTION', elements=[element]) + element = etree.Element("MESSAGE_ID", attrib={"value": str(message_id)}) + return self._control_tag( + "RIB_INFO", "TRIGGER_L2_COLLECTION", elements=[element] + ) def trigger_test_event(self, message_id): """Trigger a test service event submission to the Insight Remote Support server.""" - element = etree.Element('MESSAGE_ID', attrib={'value': str(message_id)}) - return self._control_tag('RIB_INFO', 'TRIGGER_TEST_EVENT', elements=[element]) + element = etree.Element("MESSAGE_ID", attrib={"value": str(message_id)}) + return self._control_tag("RIB_INFO", "TRIGGER_TEST_EVENT", elements=[element]) def uid_control(self, uid=False): """Turn the UID light on ("Yes") or off ("No")""" if isinstance(uid, basestring): - uid = {'on': True, 'yes': True, 'off': False, 'no': False}.get(uid.lower(), uid) - uid = ['No', 'Yes'][bool(uid)] - return self._control_tag('SERVER_INFO', 'UID_CONTROL', attrib={"UID": uid.title()}) + uid = {"on": True, "yes": True, "off": False, "no": False}.get( + uid.lower(), uid + ) + uid = ["No", "Yes"][bool(uid)] + return self._control_tag( + "SERVER_INFO", "UID_CONTROL", attrib={"UID": uid.title()} + ) def update_rib_firmware(self, filename=None, version=None, progress=None): """Upload new RIB firmware, either specified by filename (.bin or - .scexe) or version number. Use "latest" as version number to - download and use the latest available firmware. + .scexe) or version number. Use "latest" as version number to + download and use the latest available firmware. - API note: + API note: - As this function may take a while, you can choose to receive - progress messages by passing a callable in the progress parameter. - This callable will be called many times to inform you about upload - and flash progress.""" + As this function may take a while, you can choose to receive + progress messages by passing a callable in the progress parameter. + This callable will be called many times to inform you about upload + and flash progress.""" if self.delayed: raise IloError("Cannot run firmware update in delayed mode") @@ -2050,8 +2764,8 @@ def update_rib_firmware(self, filename=None, version=None, progress=None): self._detect_protocol() # Backwards compatibility - if filename == 'latest': - version = 'latest' + if filename == "latest": + version = "latest" filename = None if filename and version: @@ -2061,66 +2775,85 @@ def update_rib_firmware(self, filename=None, version=None, progress=None): raise ValueError("Supply a filename or a version number") current_version = self.get_fw_version() - ilo = current_version['management_processor'].lower() + ilo = current_version["management_processor"].lower() if not filename: config = hpilo_fw.config(self.firmware_mirror) - if version == 'latest': + if version == "latest": if ilo not in config: - raise IloError("Cannot update %s to the latest version automatically" % ilo) - version = config[ilo]['version'] - iversion = '%s %s' % (ilo, version) + raise IloError( + "Cannot update %s to the latest version automatically" % ilo + ) + version = config[ilo]["version"] + iversion = "%s %s" % (ilo, version) if iversion not in config: raise ValueError("Unknown firmware version: %s" % version) - if current_version['firmware_version'] >= version: + if current_version["firmware_version"] >= version: return "Already up-to-date" hpilo_fw.download(iversion, progress=progress) - filename = config[iversion]['file'] + filename = config[iversion]["file"] else: filename = hpilo_fw.parse(filename, ilo) fwlen = os.path.getsize(filename) - root, inner = self._root_element('RIB_INFO', MODE='write') - etree.SubElement(inner, 'TPM_ENABLED', VALUE='Yes') - inner = etree.SubElement(inner, 'UPDATE_RIB_FIRMWARE', IMAGE_LOCATION=filename, IMAGE_LENGTH=str(fwlen)) + root, inner = self._root_element("RIB_INFO", MODE="write") + etree.SubElement(inner, "TPM_ENABLED", VALUE="Yes") + inner = etree.SubElement( + inner, + "UPDATE_RIB_FIRMWARE", + IMAGE_LOCATION=filename, + IMAGE_LENGTH=str(fwlen), + ) if self.protocol == ILO_LOCAL: return self._request(root, progress)[1] elif self.protocol == ILO_RAW: - inner.tail = '$EMBED:%s$' % filename + inner.tail = "$EMBED:%s$" % filename return self._request(root, progress)[1] else: self._upload_file(filename, progress) return self._request(root, progress)[1] - def xmldata(self, item='all'): + def xmldata(self, item="all"): """Get basic discovery data which all iLO versions expose over - unauthenticated https. The default item to query is 'all'. Despite - its name, it does not return all information. To get license - information, use 'cpqkey' as argument.""" + unauthenticated https. The default item to query is 'all'. Despite + its name, it does not return all information. To get license + information, use 'cpqkey' as argument.""" if self.delayed: raise IloError("xmldata is not compatible with delayed mode") - if item.lower() not in ('all', 'cpqkey'): - raise IloError("unsupported xmldata argument '%s', must be 'all' or 'cpqkey'" % item) + if item.lower() not in ("all", "cpqkey"): + raise IloError( + "unsupported xmldata argument '%s', must be 'all' or 'cpqkey'" % item + ) if self.read_response: with open(self.read_response) as fd: data = fd.read() else: - url = 'https://%s:%s/xmldata?item=%s' % (self.hostname, self.port, item) + url = "https://%s:%s/xmldata?item=%s" % (self.hostname, self.port, item) if self.ssl_context: - opener = urllib2.build_opener(urllib2.ProxyHandler({}), urllib2.HTTPSHandler(context=self.ssl_context)) - elif hasattr(ssl, 'create_default_context'): + opener = urllib2.build_opener( + urllib2.ProxyHandler({}), + urllib2.HTTPSHandler(context=self.ssl_context), + ) + elif hasattr(ssl, "create_default_context"): ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE - opener = urllib2.build_opener(urllib2.ProxyHandler({}), urllib2.HTTPSHandler(context=ctx)) + opener = urllib2.build_opener( + urllib2.ProxyHandler({}), urllib2.HTTPSHandler(context=ctx) + ) else: opener = urllib2.build_opener(urllib2.ProxyHandler({})) req = opener.open(url, None, self.timeout) data = req.read() - self._debug(1, str(req.headers).rstrip() + "\n\n" + data.decode('ascii', 'iloxml_replace')) + self._debug( + 1, + str(req.headers).rstrip() + + "\n\n" + + data.decode("ascii", "iloxml_replace"), + ) if self.save_response: - fd = open(self.save_response, 'a') + fd = open(self.save_response, "a") fd.write(data) fd.close() return self._element_children_to_dict(etree.fromstring(data)) @@ -2129,71 +2862,81 @@ def _parse_infra2_XXXX(self, element, key, ctag): ret = {key: []} for elt in element: tag = elt.tag.lower() - if tag == 'bays': - ret['bays'] = self._element_to_list(elt) + if tag == "bays": + ret["bays"] = self._element_to_list(elt) elif tag == ctag: ret[key].append(self._element_children_to_dict(elt)) else: ret[tag] = elt.text return {key: ret} - _parse_infra2_blades = lambda self, element: self._parse_infra2_XXXX(element, 'blades', 'blade') - _parse_infra2_switches = lambda self, element: self._parse_infra2_XXXX(element, 'switches', 'switch') - _parse_infra2_managers = lambda self, element: self._parse_infra2_XXXX(element, 'managers', 'manager') - _parse_infra2_lcds = lambda self, element: self._parse_infra2_XXXX(element, 'lcds', 'lcd') - _parse_infra2_fans = lambda self, element: self._parse_infra2_XXXX(element, 'fans', 'fan') + _parse_infra2_blades = lambda self, element: self._parse_infra2_XXXX( + element, "blades", "blade" + ) + _parse_infra2_switches = lambda self, element: self._parse_infra2_XXXX( + element, "switches", "switch" + ) + _parse_infra2_managers = lambda self, element: self._parse_infra2_XXXX( + element, "managers", "manager" + ) + _parse_infra2_lcds = lambda self, element: self._parse_infra2_XXXX( + element, "lcds", "lcd" + ) + _parse_infra2_fans = lambda self, element: self._parse_infra2_XXXX( + element, "fans", "fan" + ) def _parse_infra2_power(self, element): - ret = self._parse_infra2_XXXX(element, 'power', 'powersupply') - ret['power']['powersupply'] = ret['power'].pop('power') + ret = self._parse_infra2_XXXX(element, "power", "powersupply") + ret["power"]["powersupply"] = ret["power"].pop("power") return ret def _parse_blade_portmap(self, element): - ret = {'mezz': []} + ret = {"mezz": []} for elt in element: - if elt.tag.lower() == 'mezz': - ret['mezz'].append(self._element_children_to_dict(elt)) - elif elt.tag.lower() == 'status': + if elt.tag.lower() == "mezz": + ret["mezz"].append(self._element_children_to_dict(elt)) + elif elt.tag.lower() == "status": ret[elt.tag.lower()] = elt.text.strip() - return {'portmap': ret} + return {"portmap": ret} def _parse_mezz_slot(self, element): - ret = {'port': []} + ret = {"port": []} for elt in element: - if elt.tag.lower() == 'port': - ret['port'].append(self._element_children_to_dict(elt)) - elif elt.tag.lower() == 'type': + if elt.tag.lower() == "port": + ret["port"].append(self._element_children_to_dict(elt)) + elif elt.tag.lower() == "type": ret[elt.tag.lower()] = elt.text.strip() - return {'slot': ret} + return {"slot": ret} _parse_portmap_slot = _parse_mezz_slot def _parse_mezz_device(self, element): - ret = {'port': []} + ret = {"port": []} for elt in element: - if elt.tag.lower() == 'port': - ret['port'].append(self._element_children_to_dict(elt)) + if elt.tag.lower() == "port": + ret["port"].append(self._element_children_to_dict(elt)) else: ret[elt.tag.lower()] = elt.text.strip() - return {'device': ret} + return {"device": ret} def _parse_temps_temp(self, element): - ret = {'thresholds': []} + ret = {"thresholds": []} for elt in element: - if elt.tag.lower() == 'threshold': - ret['thresholds'].append(self._element_children_to_dict(elt)) + if elt.tag.lower() == "threshold": + ret["thresholds"].append(self._element_children_to_dict(elt)) else: ret[elt.tag.lower()] = elt.text return ret xmldata_ectd = { - 'hsi': ('virtual',), - 'bladesystem': ('manager',), - 'infra2': ('diag', 'dim', 'vcm', 'vm'), - 'blade': ('bay', 'diag', 'portmap', 'power', 'vmstat'), - 'switch': ('bay', 'diag', 'portmap', 'power'), - 'manager': ('bay', 'diag', 'power'), - 'lcd': ('bay', 'diag'), - 'fan': ('bay',), - 'powersupply': ('bay', 'diag'), + "hsi": ("virtual",), + "bladesystem": ("manager",), + "infra2": ("diag", "dim", "vcm", "vm"), + "blade": ("bay", "diag", "portmap", "power", "vmstat"), + "switch": ("bay", "diag", "portmap", "power"), + "manager": ("bay", "diag", "power"), + "lcd": ("bay", "diag"), + "fan": ("bay",), + "powersupply": ("bay", "diag"), } diff --git a/hpilo_fw.py b/hpilo_fw.py index c7df6fd4..aa4332ea 100644 --- a/hpilo_fw.py +++ b/hpilo_fw.py @@ -8,6 +8,7 @@ import os import sys from zipfile import ZipFile + PY3 = sys.version_info[0] >= 3 if PY3: @@ -17,17 +18,19 @@ import urllib2 import ConfigParser -GZIP_CONSTANT = b'\x1f\x8b' +GZIP_CONSTANT = b"\x1f\x8b" _config = None + + def config(mirror=None): global _config if not _config: if mirror: - conf = _download(mirror + 'firmware.conf') + conf = _download(mirror + "firmware.conf") else: - conf = _download('https://seveas.github.io/python-hpilo/firmware.conf') - conf = conf.decode('ascii') + conf = _download("https://seveas.github.io/python-hpilo/firmware.conf") + conf = conf.decode("ascii") parser = ConfigParser.ConfigParser() parser.readfp(io.StringIO(conf)) _config = {} @@ -37,65 +40,76 @@ def config(mirror=None): _config[section][option] = parser.get(section, option) if mirror: for section in _config: - _config[section]['url'] = mirror + _config[section]['file'] + _config[section]["url"] = mirror + _config[section]["file"] return _config -def download(ilo, path=None, progress = lambda txt: None): + +def download(ilo, path=None, progress=lambda txt: None): if not path: path = os.getcwd() conf = config() - if not os.path.exists(os.path.join(path, conf[ilo]['file'])): - msg = "Downloading %s firmware version %s" % (ilo.split()[0], conf[ilo]['version']) + if not os.path.exists(os.path.join(path, conf[ilo]["file"])): + msg = "Downloading %s firmware version %s" % ( + ilo.split()[0], + conf[ilo]["version"], + ) progress(msg) - data = _download(conf[ilo]['url'], lambda txt: progress('%s %s' % (msg, txt))) - if conf[ilo]['url'].endswith('.bin'): - with open(os.path.join(path, conf[ilo]['file']), 'wb') as fd: + data = _download(conf[ilo]["url"], lambda txt: progress("%s %s" % (msg, txt))) + if conf[ilo]["url"].endswith(".bin"): + with open(os.path.join(path, conf[ilo]["file"]), "wb") as fd: fd.write(data) - elif conf[ilo]['url'].endswith('.fwpkg'): + elif conf[ilo]["url"].endswith(".fwpkg"): with ZipFile(io.BytesIO(data)) as zipObj: - zipObj.extract(conf[ilo]['file'], path) + zipObj.extract(conf[ilo]["file"], path) else: - _parse(data, path, conf[ilo]['file']) + _parse(data, path, conf[ilo]["file"]) return True return False + def parse(fwfile, ilo): - fd = open(fwfile, 'rb') + fd = open(fwfile, "rb") data = fd.read() fd.close() - if b'_SKIP=' in data: + if b"_SKIP=" in data: # scexe file fwfile = _parse(data, os.getcwd()) return fwfile + def _download(url, progress=lambda txt: None): req = urllib2.urlopen(url) - size = int(req.headers['Content-Length']) + size = int(req.headers["Content-Length"]) if size < 16384: return req.read() downloaded = 0 - data = b'' + data = b"" while downloaded < size: new = req.read(16384) data += new downloaded += len(new) - progress('%d/%d bytes (%d%%)' % (downloaded, size, downloaded*100.0/size)) + progress("%d/%d bytes (%d%%)" % (downloaded, size, downloaded * 100.0 / size)) sys.stdout.flush() return data + def _parse(scexe, path, filename=None): # An scexe is a shell script with an embedded compressed tarball. Find the tarball. - skip_start = scexe.index(b'_SKIP=') + 6 - skip_end = scexe.index(b'\n', skip_start) + skip_start = scexe.index(b"_SKIP=") + 6 + skip_end = scexe.index(b"\n", skip_start) skip = int(scexe[skip_start:skip_end]) - 1 - tarball = scexe.split(b'\n', skip)[-1] + tarball = scexe.split(b"\n", skip)[-1] # Now uncompress it if tarball[:2] != GZIP_CONSTANT: raise ValueError("scexe file seems corrupt") - tf = tarfile.open(name="bogus_name_for_old_python_versions", fileobj=io.BytesIO(tarball), mode='r:gz') - filenames = [x for x in tf.getnames() if x.endswith('.bin')] + tf = tarfile.open( + name="bogus_name_for_old_python_versions", + fileobj=io.BytesIO(tarball), + mode="r:gz", + ) + filenames = [x for x in tf.getnames() if x.endswith(".bin")] if not filename or filename not in filenames: if len(filenames) != 1: raise ValueError("scexe file seems corrupt") diff --git a/setup.py b/setup.py index eb2e9bac..226df2d1 100755 --- a/setup.py +++ b/setup.py @@ -2,24 +2,25 @@ from setuptools import setup -setup(name = "python-hpilo", - version = "4.4.3", - author = "Dennis Kaarsemaker", - author_email = "dennis@kaarsemaker.net", - url = "http://github.com/seveas/python-hpilo", - description = "iLO automation from python or shell", - py_modules = ["hpilo", "hpilo_fw"], - scripts = ["hpilo_cli"], - classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'License :: OSI Approved :: Apache Software License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: System :: Hardware', - 'Topic :: System :: Systems Administration', - 'Topic :: System :: Networking', - ] +setup( + name="python-hpilo", + version="4.4.3", + author="Dennis Kaarsemaker", + author_email="dennis@kaarsemaker.net", + url="http://github.com/seveas/python-hpilo", + description="iLO automation from python or shell", + py_modules=["hpilo", "hpilo_fw"], + scripts=["hpilo_cli"], + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: GNU General Public License (GPL)", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: System :: Hardware", + "Topic :: System :: Systems Administration", + "Topic :: System :: Networking", + ], ) diff --git a/tests/test_boot.py b/tests/test_boot.py index 0ba159b9..e7fa4e10 100644 --- a/tests/test_boot.py +++ b/tests/test_boot.py @@ -3,6 +3,7 @@ from utils import * import random + class BootTests(IloTestCase): def test_persistent_boot(self, ilo): old = ilo.get_persistent_boot() @@ -26,5 +27,6 @@ def test_one_time_boot(self, ilo): finally: ilo.set_one_time_boot(old) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_delayed.py b/tests/test_delayed.py index 0a439b99..692f82e3 100755 --- a/tests/test_delayed.py +++ b/tests/test_delayed.py @@ -2,9 +2,10 @@ from utils import * + class DelayedTests(IloTestCase): def test_delayed_calls(self, ilo): - uid = {'ON': 'Yes', 'OFF': 'No'}[ilo.get_uid_status()] + uid = {"ON": "Yes", "OFF": "No"}[ilo.get_uid_status()] non_delayed = [ ilo.get_all_users(), ilo.get_global_settings(), @@ -19,5 +20,6 @@ def test_delayed_calls(self, ilo): ilo.delayed = False self.assertEquals(non_delayed, delayed) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_global_settings.py b/tests/test_global_settings.py index c9fb14b9..efd0f048 100644 --- a/tests/test_global_settings.py +++ b/tests/test_global_settings.py @@ -2,22 +2,21 @@ from utils import * + class GlobalSettingsTests(IloTestCase): def test_mod_global_settings(self, ilo): old = ilo.get_global_settings() try: - ilo.mod_global_settings( - f8_login_required=True, - min_password=3 - ) + ilo.mod_global_settings(f8_login_required=True, min_password=3) new = ilo.get_global_settings() - self.assertEqual(new['f8_login_required'], True) - self.assertEqual(new['min_password'], 3) + self.assertEqual(new["f8_login_required"], True) + self.assertEqual(new["min_password"], 3) finally: ilo.mod_global_settings( - f8_login_required=old['f8_login_required'], - min_password=old['min_password'] + f8_login_required=old["f8_login_required"], + min_password=old["min_password"], ) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_languages.py b/tests/test_languages.py index af5550dc..25afd394 100644 --- a/tests/test_languages.py +++ b/tests/test_languages.py @@ -2,9 +2,11 @@ from utils import * + class LanguageTests(IloTestCase): def test_languages(self, ilo): - ilo.set_language(ilo.get_language()['lang_id']) + ilo.set_language(ilo.get_language()["lang_id"]) + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_logs.py b/tests/test_logs.py index f18b0050..fbe24d0a 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -3,6 +3,7 @@ from utils import * import time + class LogTests(IloTestCase): def test_ilo_event_log(self, ilo): ilo.clear_ilo_event_log() @@ -11,5 +12,6 @@ def test_ilo_event_log(self, ilo): self.assertTrue(type(log) != dict) self.assertTrue(len(log) <= 3) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_network_settings.py b/tests/test_network_settings.py index 7d285cf2..fb386251 100644 --- a/tests/test_network_settings.py +++ b/tests/test_network_settings.py @@ -2,20 +2,26 @@ from utils import * + class NetworkTests(IloTestCase): def test_mod_network_settings(self, ilo): old = ilo.get_network_settings() try: - ilo.mod_network_settings(ter_dns_server='10.1.1.1') + ilo.mod_network_settings(ter_dns_server="10.1.1.1") self.reset_delay(ilo) new = ilo.get_network_settings() - self.assertEquals(new['ter_dns_server'], '10.1.1.1') + self.assertEquals(new["ter_dns_server"], "10.1.1.1") finally: - ilo.mod_network_settings(ter_dns_server='' if old['ter_dns_server'] == '0.0.0.0' else old['ter_dns_server']) + ilo.mod_network_settings( + ter_dns_server="" + if old["ter_dns_server"] == "0.0.0.0" + else old["ter_dns_server"] + ) self.reset_delay(ilo) def test_ipv6_routes(self, ilo): - self.require_ilo(ilo, 'ilo3:1.50', 'ilo4:1.20') + self.require_ilo(ilo, "ilo3:1.50", "ilo4:1.20") + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_requests.py b/tests/test_requests.py index 1c9d070e..7d86dc39 100755 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -2,9 +2,10 @@ from utils import * + class RequestsTestMeta(type): def __new__(cls, name, parents, attrs): - root = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'xml') + root = os.path.join(os.path.dirname(os.path.abspath(__file__)), "xml") for machine in os.listdir(root): mdir = os.path.join(root, machine) if not os.path.isdir(mdir): @@ -12,27 +13,30 @@ def __new__(cls, name, parents, attrs): files = os.listdir(mdir) for argsfile in files: - if not argsfile.endswith('.args'): + if not argsfile.endswith(".args"): continue - method = argsfile[:argsfile.find('-')] - reqfile = argsfile.replace('.args', '.request') - reqtmpfile = argsfile.replace('.args', '.request.tmp') + method = argsfile[: argsfile.find("-")] + reqfile = argsfile.replace(".args", ".request") + reqtmpfile = argsfile.replace(".args", ".request.tmp") argspath = os.path.join(mdir, argsfile) reqpath = os.path.join(mdir, reqfile) reqtmppath = os.path.join(mdir, reqtmpfile) - fname = 'test_%s_%s' % (machine, argsfile[:-5]) - fname = re.sub('[^a-zA-Z_0-9]', '_', fname).lower() - attrs[fname] = eval("lambda self: self._test_request('%s', '%s', '%s', '%s', '%s')" % - (machine, method, argspath, reqpath, reqtmppath)) + fname = "test_%s_%s" % (machine, argsfile[:-5]) + fname = re.sub("[^a-zA-Z_0-9]", "_", fname).lower() + attrs[fname] = eval( + "lambda self: self._test_request('%s', '%s', '%s', '%s', '%s')" + % (machine, method, argspath, reqpath, reqtmppath) + ) return super(RequestsTestMeta, cls).__new__(cls, name, parents, attrs) + class RequestsTest(unittest.TestCase): __metaclass__ = RequestsTestMeta maxDiff = None def _test_request(self, machine, method, argsfile, reqfile, reqtmpfile): - ilo = hpilo.Ilo('nonexistent-machine','Administrator','TestPassword') - if 'ilo3' in machine.lower() or 'ilo4' in machine.lower(): + ilo = hpilo.Ilo("nonexistent-machine", "Administrator", "TestPassword") + if "ilo3" in machine.lower() or "ilo4" in machine.lower(): ilo.protocol = hpilo.ILO_HTTP else: ilo.protocol = hpilo.ILO_RAW @@ -56,5 +60,6 @@ def _test_request(self, machine, method, argsfile, reqfile, reqtmpfile): self.assertMultiLineEqual(old, new) os.unlink(reqtmpfile) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_responses.py b/tests/test_responses.py index 146b1763..c7c89178 100755 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -3,9 +3,10 @@ from utils import * import pprint + class ResponsesTestMeta(type): def __new__(cls, name, parents, attrs): - root = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'xml') + root = os.path.join(os.path.dirname(os.path.abspath(__file__)), "xml") for machine in os.listdir(root): mdir = os.path.join(root, machine) if not os.path.isdir(mdir): @@ -13,28 +14,32 @@ def __new__(cls, name, parents, attrs): files = os.listdir(mdir) for rawfile in files: - if not rawfile.endswith('.raw'): + if not rawfile.endswith(".raw"): continue - method = rawfile.replace('.raw', '') - parsedfile = rawfile.replace('.raw', '.parsed') + method = rawfile.replace(".raw", "") + parsedfile = rawfile.replace(".raw", ".parsed") rawpath = os.path.join(mdir, rawfile) parsedpath = os.path.join(mdir, parsedfile) - fname = 'test_%s_%s' % (machine, method) - fname = re.sub('[^a-zA-Z_0-9]', '_', fname).lower() - attrs[fname] = eval("lambda self: self._test_response('%s', '%s', '%s', '%s')" % - (machine, method, rawpath, parsedpath)) + fname = "test_%s_%s" % (machine, method) + fname = re.sub("[^a-zA-Z_0-9]", "_", fname).lower() + attrs[fname] = eval( + "lambda self: self._test_response('%s', '%s', '%s', '%s')" + % (machine, method, rawpath, parsedpath) + ) return super(ResponsesTestMeta, cls).__new__(cls, name, parents, attrs) + class ResponsesTest(unittest.TestCase): __metaclass__ = ResponsesTestMeta maxDiff = None method_args = { - 'get_user': ['Administrator'], - 'get_federation_group': ['slartibartfast'], + "get_user": ["Administrator"], + "get_federation_group": ["slartibartfast"], } + def _test_response(self, machine, method, rawfile, parsedfile): - ilo = hpilo.Ilo('nonexistent-machine','Administrator','TestPassword') - if 'ilo3' in machine.lower() or 'ilo4' in machine.lower(): + ilo = hpilo.Ilo("nonexistent-machine", "Administrator", "TestPassword") + if "ilo3" in machine.lower() or "ilo4" in machine.lower(): ilo.protocol = hpilo.ILO_HTTP else: ilo.protocol = hpilo.ILO_RAW @@ -45,10 +50,11 @@ def _test_response(self, machine, method, rawfile, parsedfile): self.assertRaises(hpilo.IloError, getattr(ilo, method), *args) return response = getattr(ilo, method)(*args) - new = pprint.pformat(response) + '\n' + new = pprint.pformat(response) + "\n" with open(parsedfile) as fd: old = fd.read() self.assertMultiLineEqual(old, new) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_snmp.py b/tests/test_snmp.py index 127d45ed..1c9dc9a1 100644 --- a/tests/test_snmp.py +++ b/tests/test_snmp.py @@ -2,19 +2,23 @@ from utils import * + class SnmpTests(IloTestCase): def test_mod_snmp_im_settings(self, ilo): old = ilo.get_snmp_im_settings() try: - ilo.mod_snmp_im_settings(snmp_address_3='10.42.42.42', rib_traps=True) + ilo.mod_snmp_im_settings(snmp_address_3="10.42.42.42", rib_traps=True) new = ilo.get_snmp_im_settings() - self.assertEqual(new['snmp_address_3'], '10.42.42.42') - self.assertEqual(new['rib_traps'], True) + self.assertEqual(new["snmp_address_3"], "10.42.42.42") + self.assertEqual(new["rib_traps"], True) finally: - ilo.mod_snmp_im_settings(snmp_address_3=old['snmp_address_3'], rib_traps=old['rib_traps']) + ilo.mod_snmp_im_settings( + snmp_address_3=old["snmp_address_3"], rib_traps=old["rib_traps"] + ) def test_snmp_user_profiles(self, ilo): - self.require_ilo(ilo, 'ilo4') + self.require_ilo(ilo, "ilo4") + -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/tests/test_uid.py b/tests/test_uid.py index f8455e3f..8b8acadb 100644 --- a/tests/test_uid.py +++ b/tests/test_uid.py @@ -2,16 +2,18 @@ from utils import * + class UidTests(IloTestCase): def test_uid(self, ilo): old = ilo.get_uid_status() - new = {'ON': 'No', 'OFF': 'Yes'}[old] - new2 = {'ON': 'OFF', 'OFF': 'ON'}[old] + new = {"ON": "No", "OFF": "Yes"}[old] + new2 = {"ON": "OFF", "OFF": "ON"}[old] try: ilo.uid_control(uid=new) self.assertEqual(new2, ilo.get_uid_status()) finally: ilo.uid_control(uid=old) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_users.py b/tests/test_users.py index dfe753b4..3463bf6f 100755 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -3,8 +3,9 @@ from utils import * import random + class UserTests(IloTestCase): - test_user_login = 'ilotestuser' + test_user_login = "ilotestuser" @classmethod def setUpClass(cls): @@ -13,38 +14,52 @@ def setUpClass(cls): ilo.delete_user(cls.test_user_login) except hpilo.IloUserNotFound: pass + tearDownClass = setUpClass def test_user_creation(self, ilo): - alphabet = 'abcdefghijklmnopqrstuvwxyz' + alphabet = "abcdefghijklmnopqrstuvwxyz" alphabet += alphabet.upper() - password = ''.join([random.choice(alphabet) for i in range(random.randint(20,30))]) - ilo.add_user(self.test_user_login, 'python-hpilo test user', password, - admin_priv=False, remote_cons_priv=False, reset_server_priv=False, - virtual_media_priv=False, config_ilo_priv=False) + password = "".join( + [random.choice(alphabet) for i in range(random.randint(20, 30))] + ) + ilo.add_user( + self.test_user_login, + "python-hpilo test user", + password, + admin_priv=False, + remote_cons_priv=False, + reset_server_priv=False, + virtual_media_priv=False, + config_ilo_priv=False, + ) user = ilo.get_user(self.test_user_login) - self.assertFalse(user['admin_priv']) - self.assertFalse(user['remote_cons_priv']) + self.assertFalse(user["admin_priv"]) + self.assertFalse(user["remote_cons_priv"]) def test_user_manipulation(self, ilo): ilo.mod_user(self.test_user_login, remote_cons_priv=True) user = ilo.get_user(self.test_user_login) - self.assertFalse(user['admin_priv']) - self.assertTrue(user['remote_cons_priv']) + self.assertFalse(user["admin_priv"]) + self.assertTrue(user["remote_cons_priv"]) def test_user_ssh_keys(self, ilo): - ssh_key = ''.join(['ssh-dss AAAAB3NzaC1kc3MAAACBAIpNY5fvLSS3MCjGNKjuWH', - 'rFGR5J6vLqdqIrXttTz7o6GWtmyxcC0Mlp2c/h1bMfvUiKDvDp', - '+5T7SGo/2R+aXLaPwYtm6eBPEBU2CgVTnpeVELDeaJ/tr0kTL/', - 'PKMHZDFgT9c7/hOiWr4amlGvuxs60MP/xs4jWaxLxabhjiRoCL', - 'AAAAFQChDEFySo74rpPNNWfvJHgiylTbRQAAAIEAgo8UQqXP7g', - 'MTAUdHTqlzoTnj3loc4ZTnf3W6jr25cs5XaXNnRtadfw0G4VWa', - 'S/uDyNhsq/o2nFrhWTwAvojWSe4C5MDdGGerktL1ZY/QfoxB0d', - '7aK/dlHd1iOVpGahCqyzmhEDmEnq6TWd6cBVHNVcryLEJVVtaf', - '8QmJlwS+XkIAAACAJGnuO6ZJ1S2AMOY1uOpov/srTyuu6Pxtcn', - 'HsHA5wNoNQFcYElnDndJUfMAPi0vzODntHoiOGdrX3RcjxSAB5', - 'lAgNZwFnwGWoAa8UIQlX+GwDYAIk+8G36tmHRgtl7xJlFqs9W6', - 'BhrJEmfL4ubWCPXl/yMDrrLnMQuV3Mg0DNVSg= Test key']) + ssh_key = "".join( + [ + "ssh-dss AAAAB3NzaC1kc3MAAACBAIpNY5fvLSS3MCjGNKjuWH", + "rFGR5J6vLqdqIrXttTz7o6GWtmyxcC0Mlp2c/h1bMfvUiKDvDp", + "+5T7SGo/2R+aXLaPwYtm6eBPEBU2CgVTnpeVELDeaJ/tr0kTL/", + "PKMHZDFgT9c7/hOiWr4amlGvuxs60MP/xs4jWaxLxabhjiRoCL", + "AAAAFQChDEFySo74rpPNNWfvJHgiylTbRQAAAIEAgo8UQqXP7g", + "MTAUdHTqlzoTnj3loc4ZTnf3W6jr25cs5XaXNnRtadfw0G4VWa", + "S/uDyNhsq/o2nFrhWTwAvojWSe4C5MDdGGerktL1ZY/QfoxB0d", + "7aK/dlHd1iOVpGahCqyzmhEDmEnq6TWd6cBVHNVcryLEJVVtaf", + "8QmJlwS+XkIAAACAJGnuO6ZJ1S2AMOY1uOpov/srTyuu6Pxtcn", + "HsHA5wNoNQFcYElnDndJUfMAPi0vzODntHoiOGdrX3RcjxSAB5", + "lAgNZwFnwGWoAa8UIQlX+GwDYAIk+8G36tmHRgtl7xJlFqs9W6", + "BhrJEmfL4ubWCPXl/yMDrrLnMQuV3Mg0DNVSg= Test key", + ] + ) ilo.import_ssh_key(self.test_user_login, ssh_key) ilo.delete_ssh_key(self.test_user_login) @@ -52,5 +67,6 @@ def test_z_user_deletion(self, ilo): ilo.delete_user(self.test_user_login) self.assertRaises(hpilo.IloUserNotFound, ilo.get_user, self.test_user_login) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/utils.py b/tests/utils.py index 768c9f3c..95aa680e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -11,11 +11,13 @@ import hpilo import warnings + warnings.filterwarnings("ignore", category=hpilo.IloWarning) + class FirmwareCache(object): def __init__(self): - self.cachefile = os.path.join(testroot, '.firmware_version_cache') + self.cachefile = os.path.join(testroot, ".firmware_version_cache") self.cache = {} if os.path.exists(self.cachefile): with open(self.cachefile) as fd: @@ -24,41 +26,52 @@ def __init__(self): def __getitem__(self, ilo): if ilo.hostname not in self.cache: self.cache[ilo.hostname] = ilo.get_fw_version() - with open(self.cachefile, 'w') as fd: + with open(self.cachefile, "w") as fd: json.dump(self.cache, fd) return self.cache[ilo.hostname] + + firmware_cache = FirmwareCache() + class IloTestCaseMeta(type): def __new__(cls, name, bases, attrs): - attrs['ilos'] = {} + attrs["ilos"] = {} config = ConfigParser.ConfigParser() - config.read(os.path.expanduser(os.path.join('~', '.ilo.conf'))) - login = config.get('ilo', 'login') - password = config.get('ilo', 'password') + config.read(os.path.expanduser(os.path.join("~", ".ilo.conf"))) + login = config.get("ilo", "login") + password = config.get("ilo", "password") methods = [] for attr in list(attrs.keys()): - if attr.startswith('test_') and callable(attrs[attr]): - attrs['_' + attr] = attrs.pop(attr) + if attr.startswith("test_") and callable(attrs[attr]): + attrs["_" + attr] = attrs.pop(attr) methods.append(attr[5:]) for section in config.sections(): - if not section.startswith('test '): + if not section.startswith("test "): continue key = section.split()[1] - hostname = config.get(section, 'ilo') + hostname = config.get(section, "ilo") ilo = hpilo.Ilo(hostname, login, password) ilo.firmware_version = firmware_cache[ilo] if not ilo.protocol: - ilo.protocol = hpilo.ILO_RAW if ilo.firmware_version['management_processor'].lower() in ('ilo', 'ilo2') else hpilo.ILO_HTTP + ilo.protocol = ( + hpilo.ILO_RAW + if ilo.firmware_version["management_processor"].lower() + in ("ilo", "ilo2") + else hpilo.ILO_HTTP + ) - ilo.save_response = os.path.join(testroot, 'hpilo_test_debug_output') - attrs['ilos'][key] = ilo + ilo.save_response = os.path.join(testroot, "hpilo_test_debug_output") + attrs["ilos"][key] = ilo for method in methods: - fname = re.sub('[^a-zA-Z0-9_]', '_', 'test_%s_%s' % (key, method)) - attrs[fname] = eval("lambda self: self._test_%s(self.ilos['%s'])" % (method, key)) + fname = re.sub("[^a-zA-Z0-9_]", "_", "test_%s_%s" % (key, method)) + attrs[fname] = eval( + "lambda self: self._test_%s(self.ilos['%s'])" % (method, key) + ) return super(IloTestCaseMeta, cls).__new__(cls, name, bases, attrs) + class IloTestCase(unittest.TestCase): __metaclass__ = IloTestCaseMeta maxDiff = None @@ -66,14 +79,20 @@ class IloTestCase(unittest.TestCase): def require_ilo(self, ilo, *ilos): for ilov in ilos: version = None - if ':' in ilov: - ilov, version = ilov.split(':') - if ilo.firmware_version['management_processor'].lower() == ilov: - if not version or ilo.firmware_version['firmware_version'] >= version: + if ":" in ilov: + ilov, version = ilov.split(":") + if ilo.firmware_version["management_processor"].lower() == ilov: + if not version or ilo.firmware_version["firmware_version"] >= version: return True - raise unittest.SkipTest("This test requires %s, not %s:%s" % ('|'.join(ilos), - ilo.firmware_version['management_processor'].lower(), ilo.firmware_version['firmware_version'])) + raise unittest.SkipTest( + "This test requires %s, not %s:%s" + % ( + "|".join(ilos), + ilo.firmware_version["management_processor"].lower(), + ilo.firmware_version["firmware_version"], + ) + ) def reset_delay(self, ilo): time.sleep(30) From f099e28727fafdec5bfa74cd6b32b17906a2654d Mon Sep 17 00:00:00 2001 From: Frederic Brin Date: Sat, 17 Sep 2022 12:51:10 +0200 Subject: [PATCH 2/3] Black code (Latest Black) --- docs/conf.py | 10 +++++----- hpilo.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 341b2077..90eb932b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,8 +42,8 @@ master_doc = "index" # General information about the project. -project = u"python-hpilo" -copyright = u"2011-2020, Dennis Kaarsemaker" +project = "python-hpilo" +copyright = "2011-2020, Dennis Kaarsemaker" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -183,8 +183,8 @@ ( "index", "python-hpilo.tex", - u"python-hpilo Documentation", - u"Dennis Kaarsemaker", + "python-hpilo Documentation", + "Dennis Kaarsemaker", "manual", ), ] @@ -218,5 +218,5 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ("index", "python-hpilo", u"python-hpilo Documentation", [u"Dennis Kaarsemaker"], 1) + ("index", "python-hpilo", "python-hpilo Documentation", ["Dennis Kaarsemaker"], 1) ] diff --git a/hpilo.py b/hpilo.py index ef1e76be..ebaf2a9e 100644 --- a/hpilo.py +++ b/hpilo.py @@ -105,7 +105,7 @@ def iloxml_replace(error): b = ord(b) if b < 128: break - ret += u"?" + ret += "?" warnings.warn( "Invalid ascii data found: %s, replaced with %s" % (repr(error.object[error.start : pos]), ret), From f9a37037b0bf186627b210f21a03fb1cd682c397 Mon Sep 17 00:00:00 2001 From: Frederic Brin Date: Sat, 17 Sep 2022 12:53:36 +0200 Subject: [PATCH 3/3] Black the code --- hpilo_cli | 336 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 233 insertions(+), 103 deletions(-) diff --git a/hpilo_cli b/hpilo_cli index fce19e07..eccd291b 100755 --- a/hpilo_cli +++ b/hpilo_cli @@ -16,13 +16,18 @@ PY3 = sys.version_info[0] >= 3 if PY3: import configparser as ConfigParser from urllib.error import HTTPError + basestring = str else: import ConfigParser from urllib2 import HTTPError + input = raw_input -ilo_methods = sorted([x for x in dir(hpilo.Ilo) if not x.startswith('_') and x.islower()]) +ilo_methods = sorted( + [x for x in dir(hpilo.Ilo) if not x.startswith("_") and x.islower()] +) + def main(): usage = """ @@ -30,68 +35,157 @@ def main(): %prog download_rib_firmware ilotype version [version...] """ p = optparse.OptionParser(usage=usage, add_help_option=False) - p.add_option("-l", "--login", dest="login", default=None, - help="Username to access the iLO") - p.add_option("-p", "--password", dest="password", default=None, - help="Password to access the iLO") - p.add_option("-i", "--interactive", action="store_true", default=False, - help="Prompt for username and/or password if they are not specified.") - p.add_option("-c", "--config", dest="config", default="~/.ilo.conf", - help="File containing authentication and config details", metavar="FILE") - p.add_option("-t", "--timeout", dest="timeout", type="int", default=60, - help="Timeout for iLO connections") - p.add_option("-j", "--json", dest="format", action="store_const", const="json", default="python", - help="Output a json document instead of a python dict") - p.add_option("-y", "--yaml", dest="format", action="store_const", const="yaml", default="python", - help="Output a yaml document instead of a python dict") - p.add_option("-P", "--protocol", dest="protocol", choices=("http","raw","local"), default=None, - help="Use the specified protocol instead of autodetecting") - p.add_option("-d", "--debug", dest="debug", action="count", default=0, - help="Output debug information, repeat to see all XML data") - p.add_option("-o", "--port", dest="port", type="int", default=443, - help="SSL port to connect to") - p.add_option('--ssl-verify', dest="ssl_verify", action="store_true", default=False, - help="Verify SSL certificates against the trusted CA's") - p.add_option('--ssl-ca-file', dest="ssl_ca_file", default=None, - help="CA bundle to validate iLO certificate against, instead of the system CA's") - p.add_option('--ssl-ignore-hostname', dest="ssl_ignore_hostname", action='store_true', default=False, - help="Don't check if the hostname matches the certificate when verifying SSL certificates") - p.add_option("-h", "--help", action="callback", callback=hpilo_help, - help="show this help message or help for a method") - p.add_option("-H", "--help-methods", action="callback", callback=hpilo_help_methods, - help="show all supported methods") - p.add_option('--save-response', dest="save_response", default=None, metavar='FILE', - help="Store XML output in this file") - p.add_option('--read-response', dest="read_response", default=None, metavar='FILE', - help="Read XML response from this file instead of the iLO") - p.add_option('-v', '--version', action="callback", callback=hpilo_version) + p.add_option( + "-l", "--login", dest="login", default=None, help="Username to access the iLO" + ) + p.add_option( + "-p", + "--password", + dest="password", + default=None, + help="Password to access the iLO", + ) + p.add_option( + "-i", + "--interactive", + action="store_true", + default=False, + help="Prompt for username and/or password if they are not specified.", + ) + p.add_option( + "-c", + "--config", + dest="config", + default="~/.ilo.conf", + help="File containing authentication and config details", + metavar="FILE", + ) + p.add_option( + "-t", + "--timeout", + dest="timeout", + type="int", + default=60, + help="Timeout for iLO connections", + ) + p.add_option( + "-j", + "--json", + dest="format", + action="store_const", + const="json", + default="python", + help="Output a json document instead of a python dict", + ) + p.add_option( + "-y", + "--yaml", + dest="format", + action="store_const", + const="yaml", + default="python", + help="Output a yaml document instead of a python dict", + ) + p.add_option( + "-P", + "--protocol", + dest="protocol", + choices=("http", "raw", "local"), + default=None, + help="Use the specified protocol instead of autodetecting", + ) + p.add_option( + "-d", + "--debug", + dest="debug", + action="count", + default=0, + help="Output debug information, repeat to see all XML data", + ) + p.add_option( + "-o", + "--port", + dest="port", + type="int", + default=443, + help="SSL port to connect to", + ) + p.add_option( + "--ssl-verify", + dest="ssl_verify", + action="store_true", + default=False, + help="Verify SSL certificates against the trusted CA's", + ) + p.add_option( + "--ssl-ca-file", + dest="ssl_ca_file", + default=None, + help="CA bundle to validate iLO certificate against, instead of the system CA's", + ) + p.add_option( + "--ssl-ignore-hostname", + dest="ssl_ignore_hostname", + action="store_true", + default=False, + help="Don't check if the hostname matches the certificate when verifying SSL certificates", + ) + p.add_option( + "-h", + "--help", + action="callback", + callback=hpilo_help, + help="show this help message or help for a method", + ) + p.add_option( + "-H", + "--help-methods", + action="callback", + callback=hpilo_help_methods, + help="show all supported methods", + ) + p.add_option( + "--save-response", + dest="save_response", + default=None, + metavar="FILE", + help="Store XML output in this file", + ) + p.add_option( + "--read-response", + dest="read_response", + default=None, + metavar="FILE", + help="Read XML response from this file instead of the iLO", + ) + p.add_option("-v", "--version", action="callback", callback=hpilo_version) opts, args = p.parse_args() - if opts.format == 'json': + if opts.format == "json": import json - elif opts.format == 'yaml': + elif opts.format == "yaml": import yaml # Did we get correct arguments? if len(args) < 2: p.error("Not enough arguments") - if args[1] not in ilo_methods and args[0] != 'download_rib_firmware': + if args[1] not in ilo_methods and args[0] != "download_rib_firmware": p.error("Unknown method: %s" % args[1]) config = ConfigParser.ConfigParser() if os.path.exists(os.path.expanduser(opts.config)): config.read(os.path.expanduser(opts.config)) - if args[0] == 'download_rib_firmware': - if config.has_option('firmware', 'mirror'): - hpilo_fw.config(mirror=config.get('firmware', 'mirror')) + if args[0] == "download_rib_firmware": + if config.has_option("firmware", "mirror"): + hpilo_fw.config(mirror=config.get("firmware", "mirror")) return download(args[1], args[2:]) hostname = args.pop(0) - args_ = list_split(args, '+') + args_ = list_split(args, "+") calls = [] for args in args_: @@ -99,33 +193,33 @@ def main(): # Arguments must be passed as param=value pairs that are valid arguments to the methods if sys.version_info[0] >= 3: - func = getattr(hpilo.Ilo, method)#.__func__ - argnames = func.__code__.co_varnames[1:func.__code__.co_argcount] + func = getattr(hpilo.Ilo, method) # .__func__ + argnames = func.__code__.co_varnames[1 : func.__code__.co_argcount] args_with_defaults = [] if func.__defaults__: - args_with_defaults = argnames[-len(func.__defaults__):] + args_with_defaults = argnames[-len(func.__defaults__) :] else: func = getattr(hpilo.Ilo, method).im_func - argnames = func.func_code.co_varnames[1:func.func_code.co_argcount] + argnames = func.func_code.co_varnames[1 : func.func_code.co_argcount] args_with_defaults = [] if func.func_defaults: - args_with_defaults = argnames[-len(func.func_defaults):] + args_with_defaults = argnames[-len(func.func_defaults) :] params = {} for arg in args: - if '=' not in arg: + if "=" not in arg: hpilo_help(None, None, method, None, 2) - param, val = arg.split('=', 1) + param, val = arg.split("=", 1) # Do we expect structured data? - keys = param.split('.') - if (len(keys) > 1) != (keys[0] in getattr(func, 'requires_dict', {})): + keys = param.split(".") + if (len(keys) > 1) != (keys[0] in getattr(func, "requires_dict", {})): hpilo_help(None, None, method, None, 2) if keys[0] not in argnames: hpilo_help(None, None, method, None, 2) # Optionally extract values from the config - if val.startswith('$') and '.' in val: - section, option = val[1:].split('.', 1) + if val.startswith("$") and "." in val: + section, option = val[1:].split(".", 1) if config.has_option(section, option): val = config.get(section, option) @@ -133,7 +227,7 @@ def main(): if val.isdigit(): val = int(val) else: - val = {'true': True, 'false': False}.get(val.lower(), val) + val = {"true": True, "false": False}.get(val.lower(), val) params_ = params for key in keys[:-1]: if key not in params_: @@ -153,18 +247,18 @@ def main(): password = None needs_login = bool(opts.read_response) for m, _ in calls: - if m != 'xmldata': + if m != "xmldata": needs_login = True break - if hostname == 'localhost': - opts.protocol = 'local' + if hostname == "localhost": + opts.protocol = "local" needs_login = False if needs_login: - if config.has_option('ilo', 'login'): - login = config.get('ilo', 'login') - if config.has_option('ilo', 'password'): - password = config.get('ilo', 'password') + if config.has_option("ilo", "login"): + login = config.get("ilo", "login") + if config.has_option("ilo", "password"): + password = config.get("ilo", "password") if opts.login: login = opts.login if opts.password: @@ -172,62 +266,78 @@ def main(): if not login or not password: if opts.interactive: while not login: - login = input('Login for iLO at %s: ' % hostname) + login = input("Login for iLO at %s: " % hostname) while not password: - password = getpass.getpass('Password for %s@%s:' % (login, hostname)) + password = getpass.getpass( + "Password for %s@%s:" % (login, hostname) + ) else: p.error("No login details provided") opts.protocol = { - 'http': hpilo.ILO_HTTP, - 'raw': hpilo.ILO_RAW, - 'local': hpilo.ILO_LOCAL, + "http": hpilo.ILO_HTTP, + "raw": hpilo.ILO_RAW, + "local": hpilo.ILO_LOCAL, }.get(opts.protocol, None) ssl_context = None if opts.ssl_verify: - if sys.version_info < (2,7,9): + if sys.version_info < (2, 7, 9): p.error("SSL verification is only supported in python 2.7.9 or newer") ssl_context = ssl.create_default_context(cafile=opts.ssl_ca_file) ssl_context.options &= ~ssl.OP_NO_SSLv3 ssl_context.check_hostname = not opts.ssl_ignore_hostname # import debugme - ilo = hpilo.Ilo(hostname, login, password, opts.timeout, opts.port, opts.protocol, len(calls) > 1, opts.ssl_verify, ssl_context) + ilo = hpilo.Ilo( + hostname, + login, + password, + opts.timeout, + opts.port, + opts.protocol, + len(calls) > 1, + opts.ssl_verify, + ssl_context, + ) ilo.debug = opts.debug ilo.save_response = opts.save_response ilo.read_response = opts.read_response - if config.has_option('ilo', 'hponcfg'): - ilo.hponcfg = config.get('ilo', 'hponcfg') - if config.has_option('firmware', 'mirror'): - ilo.firmware_mirror = config.get('firmware', 'mirror') + if config.has_option("ilo", "hponcfg"): + ilo.hponcfg = config.get("ilo", "hponcfg") + if config.has_option("firmware", "mirror"): + ilo.firmware_mirror = config.get("firmware", "mirror") def _q(val): if isinstance(val, basestring): - return '"%s"' % val.replace("\\","\\\\").replace('"','\\"') + return '"%s"' % val.replace("\\", "\\\\").replace('"', '\\"') else: return str(val) for method, params in calls: - if method == 'update_rib_firmware': - params['progress'] = print_progress + if method == "update_rib_firmware": + params["progress"] = print_progress results = [getattr(ilo, method)(**params)] - if 'progress' in params: - params.pop('progress')("") + if "progress" in params: + params.pop("progress")("") if len(calls) > 1: results = ilo.call_delayed() - if opts.format == 'json': + if opts.format == "json": if len(calls) == 1: results = results[0] json.dump(results, sys.stdout) - elif opts.format == 'yaml': + elif opts.format == "yaml": yaml.dump(results, sys.stdout) else: for method, params in calls: - param_str = ', '.join(["%s=%s" % (x[0], _q(x[1])) for x in params.items()]) - if method.startswith('get') or method == 'certificate_signing_request' or len(calls) == 1: + param_str = ", ".join(["%s=%s" % (x[0], _q(x[1])) for x in params.items()]) + if ( + method.startswith("get") + or method == "certificate_signing_request" + or len(calls) == 1 + ): result = results.pop(0) else: result = None @@ -240,9 +350,10 @@ def main(): print(">>> pprint(my_ilo.%s(%s))" % (method, param_str)) pprint(result) + def hpilo_help(option, opt_str, value, parser, exitcode=0): if not value: - if parser and parser.rargs and parser.rargs[0][0] != '-': + if parser and parser.rargs and parser.rargs[0][0] != "-": value = parser.rargs[0] del parser.rargs[0] @@ -251,22 +362,28 @@ def hpilo_help(option, opt_str, value, parser, exitcode=0): else: if value in ilo_methods: import re, textwrap + func = getattr(hpilo.Ilo, value).im_func code = func.func_code - args = '' + args = "" if code.co_argcount > 1: - args = code.co_varnames[:code.co_argcount] + args = code.co_varnames[: code.co_argcount] defaults = func.func_defaults or [] - args = ["%s=%s" % (x, x.upper()) for x in args[:len(args)-len(defaults)]] + \ - ["[%s=%s]" % (x,str(y)) for x, y in zip(args[len(args)-len(defaults):], defaults) if x != 'progress'] - args = ' ' + ' '.join(args[1:]) + args = [ + "%s=%s" % (x, x.upper()) for x in args[: len(args) - len(defaults)] + ] + [ + "[%s=%s]" % (x, str(y)) + for x, y in zip(args[len(args) - len(defaults) :], defaults) + if x != "progress" + ] + args = " " + " ".join(args[1:]) print(textwrap.fill("Ilo.%s%s:" % (value, args), 80)) doc = getattr(hpilo.Ilo, value).__doc__ or "No documentation" - doc = re.sub(r':[a-z]+:`(.*?)`', r'\1', doc) - if 'API note' in doc: - doc = doc[:doc.find('API note')].strip() - doc = re.sub('\s+', ' ', doc) + doc = re.sub(r":[a-z]+:`(.*?)`", r"\1", doc) + if "API note" in doc: + doc = doc[: doc.find("API note")].strip() + doc = re.sub("\s+", " ", doc) print(textwrap.fill(doc, 80)) else: print("No such method: %s" % value) @@ -275,19 +392,26 @@ def hpilo_help(option, opt_str, value, parser, exitcode=0): else: sys.exit(exitcode) + def hpilo_help_methods(option, opt_str, value, parser): - print("""Supported methods: -- %s""" % "\n- ".join(ilo_methods)) + print( + """Supported methods: +- %s""" + % "\n- ".join(ilo_methods) + ) parser.exit() + def hpilo_version(option, opt_str, value, parser): print(hpilo.__version__) sys.exit() + def print_progress(text): - sys.stdout.write('\r\033[K' + text) + sys.stdout.write("\r\033[K" + text) sys.stdout.flush() + def list_split(lst, sep): ret = [] while True: @@ -297,26 +421,28 @@ def list_split(lst, sep): ret.append(lst) break ret.append(lst[:pos]) - lst = lst[pos+1:] + lst = lst[pos + 1 :] return ret + def download(ilotype, versions): ilotype = ilotype.lower() config = hpilo_fw.config() - if ilotype == 'all': + if ilotype == "all": for ilotype in sorted(config.keys()): - if (versions == ['all']) == (' ' in ilotype): + if (versions == ["all"]) == (" " in ilotype): _download(config, ilotype) else: if not versions: _download(config, ilotype) - elif versions == ['all']: + elif versions == ["all"]: for key in sorted(config.keys()): - if key.startswith(ilotype + ' '): + if key.startswith(ilotype + " "): _download(config, key) else: for version in versions: - _download(config, '%s %s' % (ilotype, version)) + _download(config, "%s %s" % (ilotype, version)) + def _download(config, key): if key not in config: @@ -326,9 +452,13 @@ def _download(config, key): if hpilo_fw.download(key, progress=print_progress): print("") else: - print("%s firmware version %s was already downloaded" % (key.split()[0], config[key]['version'])) + print( + "%s firmware version %s was already downloaded" + % (key.split()[0], config[key]["version"]) + ) except HTTPError as exc: print("\n%s" % str(exc)) -if __name__ == '__main__': + +if __name__ == "__main__": main()