diff --git a/src/redfish/__init__.py b/src/redfish/__init__.py index 5c2185d..a207141 100644 --- a/src/redfish/__init__.py +++ b/src/redfish/__init__.py @@ -1,9 +1,10 @@ """ Redfish restful library """ __all__ = ["rest", "ris", "hpilo"] -__version__ = "4.3.0.0" +__version__ = "4.5.0.0" import logging + from redfish.rest.v1 import AuthMethod, LegacyRestClient, RedfishClient diff --git a/src/redfish/hpilo/risblobstore2.py b/src/redfish/hpilo/risblobstore2.py index 953cc47..8b01a15 100644 --- a/src/redfish/hpilo/risblobstore2.py +++ b/src/redfish/hpilo/risblobstore2.py @@ -19,26 +19,29 @@ # ---------Imports--------- +import logging import os -import sys -import struct import random import string -import logging - +import struct +import sys from ctypes import ( + POINTER, c_char_p, c_ubyte, c_uint, - cdll, - POINTER, - create_string_buffer, c_ushort, c_void_p, + cdll, + create_string_buffer, ) -from redfish.hpilo.rishpilo import HpIlo, HpIloInitialError, HpIloChifPacketExchangeError from redfish.hpilo.rishpilo import BlobReturnCodes as hpiloreturncodes +from redfish.hpilo.rishpilo import ( + HpIlo, + HpIloChifPacketExchangeError, + HpIloInitialError, +) if os.name == "nt": from ctypes import windll @@ -202,10 +205,7 @@ def create(self, key, namespace): resp = self._send_receive_raw(data) errorcode = struct.unpack(" 0: return False else: - #LOGGER.debug("Calling ChifDisableSecurity...") + # LOGGER.debug("Calling ChifDisableSecurity...") dll.ChifDisableSecurity() BlobStore2.unloadchifhandle(dll) @@ -1016,11 +963,7 @@ def checkincurrdirectory(libname): libpath = os.path.join(os.getcwd(), libpath) elif os.environ.get("LD_LIBRARY_PATH"): paths = os.getenv("LD_LIBRARY_PATH", libpath).split(";") - libpath = [ - os.path.join(pat, libname) - for pat in paths - if os.path.isfile(os.path.join(pat, libname)) - ] + libpath = [os.path.join(pat, libname) for pat in paths if os.path.isfile(os.path.join(pat, libname))] libpath = libpath[0] if libpath else libname return libpath diff --git a/src/redfish/hpilo/rishpilo.py b/src/redfish/hpilo/rishpilo.py index 1efa7a3..7e43b51 100644 --- a/src/redfish/hpilo/rishpilo.py +++ b/src/redfish/hpilo/rishpilo.py @@ -19,12 +19,11 @@ # ---------Imports--------- +import logging import os -import time import struct -import logging - -from ctypes import c_void_p, c_uint32, byref, create_string_buffer +import time +from ctypes import byref, c_uint32, c_void_p, create_string_buffer # ---------End of imports--------- # ---------Debug logger--------- @@ -113,7 +112,7 @@ def __init__(self, dll=None): self.dll = dll if LOGGER.isEnabledFor(logging.DEBUG): self.dll.enabledebugoutput() - #LOGGER.debug("Calling ChifInitialize...") + # LOGGER.debug("Calling ChifInitialize...") self.dll.ChifInitialize(None) self.dll.ChifCreate.argtypes = [c_void_p] @@ -123,25 +122,20 @@ def __init__(self, dll=None): LOGGER.debug("Calling ChifCreate...") status = self.dll.ChifCreate(byref(fhandle)) if status != BlobReturnCodes.SUCCESS: - raise HpIloInitialError( - "Error %s occurred while trying " "to create a channel." % status - ) + raise HpIloInitialError("Error %s occurred while trying " "to create a channel." % status) self.fhandle = fhandle - if not "skip_ping" in os.environ: + if "skip_ping" not in os.environ: status = self.dll.ChifPing(self.fhandle) if status != BlobReturnCodes.SUCCESS: - errmsg = ( - "Error {0} occurred while trying to open a " - "channel to iLO".format(status) - ) + errmsg = "Error {0} occurred while trying to open a " "channel to iLO".format(status) if status == BlobReturnCodes.CHIFERR_NoDriver: errmsg = "chif" elif status == BlobReturnCodes.CHIFERR_AccessDenied: errmsg = "You must be root/Administrator to use this program." raise HpIloInitialError(errmsg) - #LOGGER.debug("Calling ChifSetRecvTimeout...") + # LOGGER.debug("Calling ChifSetRecvTimeout...") self.dll.ChifSetRecvTimeout(self.fhandle, 60000) except: raise @@ -157,14 +151,10 @@ def chif_packet_exchange(self, data): buff = create_string_buffer(bytes(data)) recbuff = create_string_buffer(datarecv) - #LOGGER.debug("Calling ChifPacketExchange...") - error = self.dll.ChifPacketExchange( - self.fhandle, byref(buff), byref(recbuff), datarecv - ) + # LOGGER.debug("Calling ChifPacketExchange...") + error = self.dll.ChifPacketExchange(self.fhandle, byref(buff), byref(recbuff), datarecv) if error != BlobReturnCodes.SUCCESS: - raise HpIloChifPacketExchangeError( - "Error %s occurred while " "exchange chif packet" % error - ) + raise HpIloChifPacketExchangeError("Error %s occurred while " "exchange chif packet" % error) pkt = bytearray() diff --git a/src/redfish/rest/connections.py b/src/redfish/rest/connections.py index fbed2f7..3ca8205 100644 --- a/src/redfish/rest/connections.py +++ b/src/redfish/rest/connections.py @@ -16,16 +16,14 @@ # -*- coding: utf-8 -*- """All Connections for interacting with REST.""" -import os -import time import gzip import json import logging -import urllib3 -import certifi +import time -from urllib3 import ProxyManager, PoolManager -from urllib3.exceptions import MaxRetryError, DecodeError +import urllib3 +from urllib3 import PoolManager, ProxyManager +from urllib3.exceptions import DecodeError, MaxRetryError try: urllib3.disable_warnings() @@ -35,10 +33,14 @@ import six from six import BytesIO -from six.moves.urllib.parse import urlparse, urlencode +from six.moves.urllib.parse import urlencode, urlparse +from redfish.hpilo.risblobstore2 import ( + Blob2OverrideError, + Blob2SecurityError, + BlobStore2, +) from redfish.hpilo.rishpilo import HpIloChifPacketExchangeError -from redfish.hpilo.risblobstore2 import BlobStore2, Blob2OverrideError, Blob2SecurityError from redfish.rest.containers import RestRequest, RestResponse, RisRestResponse # ---------End of imports--------- @@ -93,13 +95,32 @@ class SecurityStateError(Exception): pass +class OneTimePasscodeError(Exception): + """Raised when OTP is sent to the registered email.""" + + pass + +class TokenExpiredError(Exception): + """Raised when OTP entered has expired.""" + + pass + +class UnauthorizedLoginAttemptError(Exception): + """Raised when Login is Unauthorized""" + + pass + class HttpConnection(object): """HTTP connection capable of authenticating with HTTPS and Http/Socks Proxies :param base_url: The URL to make HTTP calls against :type base_url: str - :param \\**client_kwargs: Arguments to pass to the connection initialization. These are """ "passed to a urllib3 `PoolManager `_. All arguments that can be passed to " "a PoolManager are valid arguments." + :param \\**client_kwargs: Arguments to pass to the connection initialization. These are" + "passed to a urllib3 `PoolManager `_. All arguments that can be passed to " + "a PoolManager are valid arguments." + """ def __init__(self, base_url, cert_data, **client_kwargs): self._conn = None @@ -107,7 +128,7 @@ def __init__(self, base_url, cert_data, **client_kwargs): self._connection_properties = client_kwargs if cert_data: if ("cert_file" in cert_data and cert_data["cert_file"]) or ( - "ca_certs" in cert_data and cert_data["ca_certs"] + "ca_certs" in cert_data and cert_data["ca_certs"] ): self._connection_properties.update({"ca_cert_data": cert_data}) self._proxy = self._connection_properties.pop("proxy", None) @@ -130,9 +151,7 @@ def _init_connection(self): if self._connection_properties.get("ca_cert_data"): LOGGER.info("Using CA cert to confirm identity.") cert_reqs = "CERT_NONE" - self._connection_properties.update( - self._connection_properties.pop("ca_cert_data") - ) + self._connection_properties.update(self._connection_properties.pop("ca_cert_data")) timeout = urllib3.util.Timeout(connect=4800, read=4800) retries = urllib3.util.Retry(connect=50, read=50, redirect=50) @@ -165,16 +184,12 @@ def _init_connection(self): pass if "timeout" not in self._connection_properties: - http = PoolManager(maxsize=50, - cert_reqs=cert_reqs, - timeout=timeout, - retries=retries, - **self._connection_properties - ) + http = PoolManager( + maxsize=50, cert_reqs=cert_reqs, timeout=timeout, retries=retries, **self._connection_properties + ) else: http = PoolManager(cert_reqs=cert_reqs, maxsize=50, retries=retries, **self._connection_properties) - self._conn = http.request def rest_request(self, path, method="GET", args=None, body=None, headers=None): @@ -222,9 +237,7 @@ def rest_request(self, path, method="GET", args=None, body=None, headers=None): gfile = gzip.GzipFile(mode="wb", fileobj=buf) try: - gfile.write( - str(body).encode("utf-8") if six.PY3 else str(body) - ) + gfile.write(str(body).encode("utf-8") if six.PY3 else str(body)) finally: gfile.close() @@ -245,11 +258,9 @@ def rest_request(self, path, method="GET", args=None, body=None, headers=None): body = urlencode(args) # TODO: ADD to the default headers? - if headers != None: + if headers is not None: headers["Accept-Encoding"] = "gzip" - restreq = RestRequest( - path, method, data=files if files else body, url=self.base_url - ) + restreq = RestRequest(path, method, data=files if files else body, url=self.base_url) if LOGGER.isEnabledFor(logging.DEBUG): try: @@ -299,7 +310,7 @@ def rest_request(self, path, method="GET", args=None, body=None, headers=None): request_args["body"] = body try: resp = self._conn(method, reqfullpath, **request_args) - except MaxRetryError as excp: + except MaxRetryError: vnic_url = "16.1.15.1" if reqfullpath.find(vnic_url) != -1: raise VnicNotEnabledError() @@ -308,14 +319,12 @@ def rest_request(self, path, method="GET", args=None, body=None, headers=None): raise DecompressResponseError() endtime = time.time() - LOGGER.info( - "Response Time to %s: %s seconds.", restreq.path, str(endtime - inittime) - ) + LOGGER.info("Response Time to %s: %s seconds.", restreq.path, str(endtime - inittime)) restresp = RestResponse(restreq, resp) -# if restresp.request.body: -# respbody = restresp.read -# respbody = respbody.replace("\\\\", "\\") + # if restresp.request.body: + # respbody = restresp.read + # respbody = respbody.replace("\\\\", "\\") if LOGGER.isEnabledFor(logging.DEBUG): headerstr = "" @@ -325,12 +334,9 @@ def rest_request(self, path, method="GET", args=None, body=None, headers=None): headerstr += "\t" + kiy + ": " + headerval + "\n" try: LOGGER.debug( - "HTTP RESPONSE for %s:\nCode:%s\nHeaders:" - "\n%s\nBody Response of %s: %s", + "HTTP RESPONSE for %s:\nCode:%s\nHeaders:" "\n%s\nBody Response of %s: %s", restresp.request.path, - str(restresp._http_response.status) - + " " - + restresp._http_response.reason, + str(restresp._http_response.status) + " " + restresp._http_response.reason, headerstr, restresp.request.path, restresp.read, @@ -389,9 +395,7 @@ def _init_connection(self, **kwargs): if isinstance(password, bytes): password = password.decode("utf-8") try: - correctcreds = BlobStore2.initializecreds( - username=username, password=password - ) + correctcreds = BlobStore2.initializecreds(username=username, password=password) bs2 = BlobStore2() if not correctcreds: security_state = int(bs2.get_security_state()) @@ -456,9 +460,7 @@ def rest_request(self, path="", method="GET", args=None, body=None, headers=None gfile = gzip.GzipFile(mode="wb", fileobj=buf) try: - gfile.write( - str(body).encode("utf-8") if six.PY3 else str(body) - ) + gfile.write(str(body).encode("utf-8") if six.PY3 else str(body)) finally: gfile.close() @@ -537,7 +539,7 @@ def rest_request(self, path="", method="GET", args=None, body=None, headers=None try: resp_txt = self._conn.rest_immediate(str1) break - except Blob2OverrideError as excp: + except Blob2OverrideError: if idx == 4: raise Blob2OverrideError(2) else: @@ -565,9 +567,7 @@ def rest_request(self, path="", method="GET", args=None, body=None, headers=None newloc = rest_response.getheader("location") newurl = urlparse(newloc) - rest_response = self.rest_request( - newurl.path, method, args, oribody, headers - ) + rest_response = self.rest_request(newurl.path, method, args, oribody, headers) try: if rest_response.getheader("content-encoding") == "gzip": @@ -586,12 +586,9 @@ def rest_request(self, path="", method="GET", args=None, body=None, headers=None headerstr += "\t" + header + ": " + headerget[header] + "\n" try: LOGGER.debug( - "Blobstore RESPONSE for %s:\nCode: %s\nHeaders:" - "\n%s\nBody of %s: %s", + "Blobstore RESPONSE for %s:\nCode: %s\nHeaders:" "\n%s\nBody of %s: %s", rest_response.request.path, - str(rest_response._http_response.status) - + " " - + rest_response._http_response.reason, + str(rest_response._http_response.status) + " " + rest_response._http_response.reason, headerstr, rest_response.request.path, rest_response.read, diff --git a/src/redfish/rest/containers.py b/src/redfish/rest/containers.py index f446b1c..ad5ff6d 100644 --- a/src/redfish/rest/containers.py +++ b/src/redfish/rest/containers.py @@ -16,16 +16,15 @@ # -*- coding: utf-8 -*- """Containers used for REST requests and responses.""" -import sys import json -import six +import sys try: from collections import OrderedDict except ImportError: from collections.abc import OrderedDict -from six import text_type, string_types, StringIO, BytesIO +from six import BytesIO, StringIO, string_types, text_type from six.moves import http_client @@ -190,11 +189,7 @@ def read(self, read): def getheaders(self): """Get all headers included in the response.""" - return ( - dict(self._http_response.headers) - if self._http_response is not None - else self._headers - ) + return dict(self._http_response.headers) if self._http_response is not None else self._headers def getheader(self, name): """Case-insensitive search for an individual header @@ -248,11 +243,7 @@ def status(self): if self._status: return self._status - return ( - self._http_response.status - if self._http_response is not None - else self._status - ) + return self._http_response.status if self._http_response is not None else self._status @property def session_key(self): diff --git a/src/redfish/rest/v1.py b/src/redfish/rest/v1.py index cb8d085..fa60f29 100644 --- a/src/redfish/rest/v1.py +++ b/src/redfish/rest/v1.py @@ -19,7 +19,6 @@ # ---------Imports--------- -import json import base64 import hashlib import logging @@ -30,6 +29,9 @@ Blobstore2Connection, HttpConnection, InvalidCredentialsError, + OneTimePasscodeError, + UnauthorizedLoginAttemptError, + TokenExpiredError, ) # ---------End of imports--------- @@ -104,11 +106,13 @@ def _build_connection(self, **conn_kwargs): _ = conn_kwargs.pop("username", None) _ = conn_kwargs.pop("password", None) _ = conn_kwargs.pop("sessionid", None) + _ = conn_kwargs.pop("login_otp", None) self.connection = HttpConnection(base_url, self._cert_data, **conn_kwargs) else: _ = conn_kwargs.pop("username", None) _ = conn_kwargs.pop("password", None) _ = conn_kwargs.pop("sessionid", None) + _ = conn_kwargs.pop("login_otp", None) self.connection = HttpConnection(base_url, self._cert_data, **conn_kwargs) def _get_req_headers(self, headers=None): @@ -209,9 +213,7 @@ def head(self, path, headers=None): :type headers: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ - return self.connection.rest_request( - path, method="HEAD", headers=self._get_req_headers(headers=headers) - ) + return self.connection.rest_request(path, method="HEAD", headers=self._get_req_headers(headers=headers)) def delete(self, path, headers=None): """Perform a DELETE request @@ -222,9 +224,7 @@ def delete(self, path, headers=None): :type args: dict :returns: A :class:`redfish.rest.containers.RestResponse` object """ - return self.connection.rest_request( - path, method="DELETE", headers=self._get_req_headers(headers=headers) - ) + return self.connection.rest_request(path, method="DELETE", headers=self._get_req_headers(headers=headers)) class RestClient(RestClientBase): @@ -256,25 +256,21 @@ def __init__( base_url=None, auth=None, ca_cert_data=None, + login_otp=None, **client_kwargs ): """Create a Rest Client object""" self.default_prefix = default_prefix self.is_redfish = is_redfish self.root = None - self.auth_type = self._get_auth_type( - auth, ca_cert_data=ca_cert_data, **client_kwargs - ) + self.auth_type = self._get_auth_type(auth, ca_cert_data=ca_cert_data, **client_kwargs) self._auth_key = None self._user_pass = (username, password) self._session_location = None self._cert_data = ca_cert_data + self.login_otp = login_otp super(RestClient, self).__init__( - username=username, - password=password, - sessionid=sessionid, - base_url=base_url, - **client_kwargs + username=username, password=password, sessionid=sessionid, base_url=base_url, login_otp=login_otp, **client_kwargs ) def __enter__(self): @@ -295,9 +291,7 @@ def _get_auth_type(self, auth_param, ca_cert_data=None, **client_kwargs): if ("ca_certs" in ca_cert_data and ca_cert_data["ca_certs"]) or ( "cert_file" in ca_cert_data and ca_cert_data["cert_file"] ): - if ( - ca_cert_data.get("cert_file") and ca_cert_data.get("key_file") - ) or ca_cert_data.get("ca_certs"): + if (ca_cert_data.get("cert_file") and ca_cert_data.get("key_file")) or ca_cert_data.get("ca_certs"): return AuthMethod.CERTIFICATE return AuthMethod.SESSION @@ -320,11 +314,7 @@ def proxy(self): @property def session_key(self): """The Client's session key, if any.""" - return ( - self._auth_key - if self.auth_type in [AuthMethod.SESSION, AuthMethod.CERTIFICATE] - else None - ) + return self._auth_key if self.auth_type in [AuthMethod.SESSION, AuthMethod.CERTIFICATE] else None @session_key.setter def session_key(self, ses_key): @@ -351,7 +341,7 @@ def session_location(self): session_loc = session_loc.replace(" ", "%20") else: parse_object = urlparse(self.base_url) - if parse_object.hostname != None: + if parse_object.hostname is not None: newurl = "https://" + parse_object.hostname session_loc = self._session_location.replace(newurl, "") else: @@ -399,9 +389,7 @@ def login_url(self): login_url = self.root.obj.links.Sessions.href finally: if not login_url: - raise ServerDownOrUnreachableError( - "Cannot locate the login url. Is this a Rest or" " Redfish server?" - ) + raise ServerDownOrUnreachableError("Cannot locate the login url. Is this a Rest or" " Redfish server?") return login_url def login(self, auth=AuthMethod.SESSION): @@ -443,18 +431,14 @@ def _get_root(self): resp = self.get(self.default_prefix) if resp.status != 200: - raise ServerDownOrUnreachableError( - "Server not reachable, " "return code: %d" % resp.status - ) + raise ServerDownOrUnreachableError("Server not reachable, " "return code: %d" % resp.status) self.root = resp def _basic_login(self): """Login using basic authentication""" LOGGER.info("Performing basic authentication.") if not self.basic_auth: - auth_key = base64.b64encode( - ("{}:{}".format(self.username, self.password)).encode("utf-8") - ).decode("utf-8") + auth_key = base64.b64encode(("{}:{}".format(self.username, self.password)).encode("utf-8")).decode("utf-8") self.basic_auth = "Basic {}".format(auth_key) headers = dict() @@ -474,23 +458,33 @@ def _session_login(self): data = dict() data["UserName"] = self.username data["Password"] = self.password + if self.login_otp is not None: + data["Token"] = self.login_otp headers = dict() resp = self.post(self.login_url, body=data, headers=headers) try: respread = resp.read respread = respread.replace("\\\\", "\\") - #LOGGER.info("%s" % respread) + # LOGGER.info("%s" % respread) except ValueError: pass LOGGER.debug("Login returned code %s: %s", resp.status, respread) self.session_key = resp.session_key self.session_location = resp.session_location + self.login_return_code = resp.status + self.login_response = respread else: self.session_key = self.connection.session_key - if not self.session_key and not resp.status == 200: + if "OneTimePasscodeSent" in self.login_response: + raise OneTimePasscodeError() + elif "UnauthorizedLogin" in self.login_response: + raise UnauthorizedLoginAttemptError("Error "+str(self.login_return_code)+". Login is unauthorized.\nPlease check the credentials/OTP entered.\n") + elif "TokenExpired" in self.login_response: + raise TokenExpiredError("Error "+str(self.login_return_code)+". The OTP entered has expired. Please enter credentials again.\n") + elif not self.session_key and not resp.status == 200: self._credential_err() else: self._user_pass = (None, None) @@ -522,17 +516,11 @@ def _get_req_headers(self, headers=None, optionalpassword=None): """ headers = headers if isinstance(headers, dict) else dict() h_list = [header.lower() for header in headers] - auth_headers = ( - True if "x-auth-token" in h_list or "authorization" in h_list else False - ) + auth_headers = True if "x-auth-token" in h_list or "authorization" in h_list else False token = self._biospassword if self._biospassword else optionalpassword if token: - token = ( - optionalpassword.encode("utf-8") - if type(optionalpassword).__name__ in "basestr" - else token - ) + token = optionalpassword.encode("utf-8") if type(optionalpassword).__name__ in "basestr" else token hash_object = hashlib.new("SHA256") hash_object.update(token) headers["X-HPRESTFULAPI-AuthToken"] = hash_object.hexdigest().upper() @@ -558,14 +546,10 @@ def get_resource_directory(self): resources = [] if response.status == 200: - sys.stdout.write( - "\tFound resource directory at /redfish/v1/resourcedirectory" + "\n\n" - ) + sys.stdout.write("\tFound resource directory at /redfish/v1/resourcedirectory" + "\n\n") resources = response.dict["Instances"] else: - sys.stderr.write( - "\tResource directory missing at /redfish/v1/resourcedirectory" + "\n" - ) + sys.stderr.write("\tResource directory missing at /redfish/v1/resourcedirectory" + "\n") return resources @@ -577,24 +561,16 @@ def get_gen(self): gencompany = next(iter(rootresp.get("Oem", {}).keys()), None) in ("Hpe", "Hp") comp = "Hp" if gencompany else None comp = "Hpe" if rootresp.get("Oem", {}).get("Hpe", None) else comp - if comp and next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerType", None): - ilogen = next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerType") - ilover = next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerFirmwareVersion") + if comp and next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType", None): + ilogen = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType") + ilover = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerFirmwareVersion") if ilogen.split(" ")[-1] == "CM": # Assume iLO 4 types in Moonshot ilogen = 4 iloversion = None else: ilogen = ilogen.split(" ")[1] - iloversion = float( - ilogen.split(" ")[-1] + "." + "".join(ilover.split(".")) - ) + iloversion = float(ilogen.split(" ")[-1] + "." + "".join(ilover.split("."))) return (ilogen, iloversion) diff --git a/src/redfish/ris/__init__.py b/src/redfish/ris/__init__.py index acd2007..bedb574 100644 --- a/src/redfish/ris/__init__.py +++ b/src/redfish/ris/__init__.py @@ -4,33 +4,29 @@ and error registries. """ -from .sharedtypes import JSONEncoder - from .ris import ( RisInstanceNotFoundError, + RisMonolith, RisMonolithMemberBase, RisMonolithMemberv100, - RisMonolith, SessionExpired, ) - +from .rmc import RmcApp from .rmc_helper import ( - UndefinedClientError, - InstanceNotFoundError, CurrentlyLoggedInError, + IdTokenError, + IloLicenseError, + InstanceNotFoundError, + InvalidSelectionError, NothingSelectedError, NothingSelectedFilterError, NothingSelectedSetError, - InvalidSelectionError, - IdTokenError, - ValidationError, - ValueChangedError, RmcCacheManager, - ScepenabledError, - IloLicenseError, RmcFileCacheManager, + ScepenabledError, + UndefinedClientError, + ValidationError, + ValueChangedError, ) - -from .rmc import RmcApp - -from .validation import ValidationManager, RegistryValidationError +from .sharedtypes import JSONEncoder +from .validation import RegistryValidationError, ValidationManager diff --git a/src/redfish/ris/gen_compat.py b/src/redfish/ris/gen_compat.py index eae108c..42e7d0d 100644 --- a/src/redfish/ris/gen_compat.py +++ b/src/redfish/ris/gen_compat.py @@ -21,11 +21,11 @@ # ---------Imports--------- import logging -from redfish import RedfishClient, LegacyRestClient +from redfish import LegacyRestClient, RedfishClient from redfish.rest.v1 import ServerDownOrUnreachableError from redfish.ris.rmc_helper import ( - UnableToObtainIloVersionError, NothingSelectedError, + UnableToObtainIloVersionError, UserNotAdminError, ) @@ -71,6 +71,7 @@ def getgen( proxy=None, ca_cert_data={}, isredfish=True, + login_otp=None, ): """Function designed to verify the servers platform. Will generate the `Typeandpathdefines` variables based on the system type that is detected. @@ -115,6 +116,7 @@ def getgen( sessionid=sessionid, proxy=proxy, ca_cert_data=ca_cert_data, + login_otp=login_otp, ) client._get_root() except ServerDownOrUnreachableError as excp: @@ -130,6 +132,7 @@ def getgen( sessionid=sessionid, proxy=proxy, ca_cert_data=ca_cert_data, + login_otp=login_otp ) restclient._get_root() # Check that the response is actually legacy rest and not a redirect @@ -146,8 +149,7 @@ def getgen( if try_count > 1: raise ServerDownOrUnreachableError( - "Server not reachable or does not support " - "HPRest or Redfish: %s\n" % str(excp) + "Server not reachable or does not support " "HPRest or Redfish \n" ) rootresp = client.root.obj @@ -160,23 +162,17 @@ def getgen( ) comp = "Hp" if self.gencompany else None comp = "Hpe" if rootresp.get("Oem", {}).get("Hpe", None) else comp - if comp and next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerType", None): - self.ilogen = next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerType") - self.ilover = next( - iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {})) - ).get("ManagerFirmwareVersion") + if comp and next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType", None): + self.ilogen = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get("ManagerType") + self.ilover = next(iter(rootresp.get("Oem", {}).get(comp, {}).get("Manager", {}))).get( + "ManagerFirmwareVersion" + ) if self.ilogen.split(" ")[-1] == "CM": # Assume iLO 4 types in Moonshot self.ilogen = 4 self.iloversion = None else: - self.iloversion = float( - self.ilogen.split(" ")[-1] + "." + "".join(self.ilover.split(".")) - ) + self.iloversion = float(self.ilogen.split(" ")[-1] + "." + "".join(self.ilover.split("."))) else: self.ilogen = int(gen) @@ -189,9 +185,7 @@ def getgen( self.noschemas = ( True - if self.rootresp - and "JsonSchemas" in self.rootresp - and not self.rootresp.get("JsonSchemas", None) + if self.rootresp and "JsonSchemas" in self.rootresp and not self.rootresp.get("JsonSchemas", None) else False ) if self.noschemas: diff --git a/src/redfish/ris/resp_handler.py b/src/redfish/ris/resp_handler.py index 0393fd9..fcb64e1 100644 --- a/src/redfish/ris/resp_handler.py +++ b/src/redfish/ris/resp_handler.py @@ -19,14 +19,20 @@ with registries available on system, otherwise will return generic error responses.""" import logging -from redfish.ris.rmc_helper import IloLicenseError, ScepenabledError from redfish.ris.ris import SessionExpired -from redfish.ris.utils import warning_handler, print_handler, get_errmsg_type, json_traversal from redfish.ris.rmc_helper import ( - IloResponseError, + EmptyRaiseForEAFP, IdTokenError, + IloLicenseError, + IloResponseError, + ScepenabledError, ValueChangedError, - EmptyRaiseForEAFP, +) +from redfish.ris.utils import ( + get_errmsg_type, + json_traversal, + print_handler, + warning_handler, ) # ---------Debug logger--------- @@ -73,27 +79,21 @@ def output_resp(self, response, dl_reg=False, verbosity=1): else: message_text = "The operation completed successfully." - if response.status < 300 and ( - response._rest_request.method == "GET" or not response.read - ): + if response.status < 300 and (response._rest_request.method == "GET" or not response.read): # for rawget if verbosity == 0: verbosity = 1 print_handler( self.verbosity_levels( - message=message_text, - response_status=response.status, - verbosity=verbosity, - dl_reg=dl_reg)) + message=message_text, response_status=response.status, verbosity=verbosity, dl_reg=dl_reg + ) + ) elif response.status == 401: raise SessionExpired() elif response.status == 403: results = response.dict["error"]["@Message.ExtendedInfo"] for result in results: - if ( - "License" in list(result.values())[0] - or "license" in list(result.values())[0] - ): + if "License" in list(result.values())[0] or "license" in list(result.values())[0]: raise IloLicenseError("") raise IdTokenError() elif response.status == 412: @@ -113,10 +113,7 @@ def output_resp(self, response, dl_reg=False, verbosity=1): if response.status == 400: results = response.dict["error"]["@Message.ExtendedInfo"] for result in results: - if ( - "License" in list(result.values())[0] - or "license" in list(result.values())[0] - ): + if "License" in list(result.values())[0] or "license" in list(result.values())[0]: raise IloLicenseError("") if "UnsupportedOperationACEEnabled" in list(result.values())[0]: raise ScepenabledError("") @@ -125,9 +122,7 @@ def output_resp(self, response, dl_reg=False, verbosity=1): else: return retdata - def message_handler( - self, response_data, verbosity=0, message_text="No Response", dl_reg=False - ): + def message_handler(self, response_data, verbosity=0, message_text="No Response", dl_reg=False): """Prints or logs parsed MessageId response based on verbosity level and returns the following message information in a list: @@ -233,11 +228,9 @@ def get_message_data(self, resp_data, dl_reg=False): try: if not dl_reg: for inst in data_extract: - if [key.lower() for key in inst.keys()] not in [ - erk.lower() for erk in err_response_keys - ]: - if "messageid" in [str(_key.lower()) for _key in inst.keys()]: - inst.update(self.get_error_messages(inst[_key])) + if [key.lower() for key in inst.keys()] not in [erk.lower() for erk in err_response_keys]: + if "messageid" in [str(key.lower()) for key in inst.keys()]: + inst.update(self.get_error_messages(inst[key])) continue finally: return data_extract @@ -344,7 +337,7 @@ def get_error_messages(self, regtype=None): if not regval and reg: reg = reg["@odata.id"].split("/") reg = reg[len(reg) - 2] - if not "biosattributeregistry" in reg.lower(): + if "biosattributeregistry" not in reg.lower(): reglist.append(reg) elif regval: reglist.append(regval) @@ -355,15 +348,10 @@ def get_error_messages(self, regtype=None): getmsg=True, currtype=reg, searchtype=self.msg_reg_type ) if messages: - errmessages.update( - messages.get(next(iter(messages)))[regtype.split(".")[-1]] - ) + errmessages.update(messages.get(next(iter(messages)))[regtype.split(".")[-1]]) if not reglist or not errmessages: raise Exception except Exception: - raise EmptyRaiseForEAFP( - "Unable to find registry schema with provided registry " - "type: %s" % regtype - ) + raise EmptyRaiseForEAFP("Unable to find registry schema with provided registry " "type: %s" % regtype) else: return errmessages diff --git a/src/redfish/ris/ris.py b/src/redfish/ris/ris.py index 1f2ec3a..32c635e 100644 --- a/src/redfish/ris/ris.py +++ b/src/redfish/ris/ris.py @@ -20,13 +20,12 @@ # ---------Imports--------- +import logging import re -from re import error as regexerr - import sys -import weakref -import logging import threading +from re import error as regexerr + # Added for py3 compatibility import six @@ -36,16 +35,15 @@ from collections.abc import OrderedDict, defaultdict from queue import Queue -from six.moves.urllib.parse import urlparse, urlunparse import jsonpath_rw import jsonpointer - from jsonpointer import set_pointer +from six.moves.urllib.parse import urlparse, urlunparse -from redfish.ris.sharedtypes import Dictable -from redfish.ris.ris_threaded import LoadWorker from redfish.rest.containers import RestRequest, StaticRestResponse +from redfish.ris.ris_threaded import LoadWorker +from redfish.ris.sharedtypes import Dictable # ---------End of imports--------- @@ -118,9 +116,7 @@ def type(self): elif self and "type" in self.resp.dict: return self.resp.dict["type"] except (AttributeError, ValueError, TypeError): - return ( - self.deftype - ) # data not yet fetched, probably empty dict, so assume deftype + return self.deftype # data not yet fetched, probably empty dict, so assume deftype return None @property @@ -393,9 +389,7 @@ def itertype(self, typeval): yield self.paths[item] types = next(typeiter, None) else: - raise RisInstanceNotFoundError( - "Unable to locate instance for '%s'\n" % typeval - ) + raise RisInstanceNotFoundError("Unable to locate instance for '%s'\n" % typeval) def typecheck(self, types): """Check if a member of given type exists in the monolith @@ -416,7 +410,7 @@ def gettypename(self, types): :type types: str :rtype: iter of major types """ - types = types[1:] if types[0] in ("#", u"#") else types + types = types[1:] if types[0] in ("#", "#") else types return iter((xt for xt in self.types if xt and types.lower() in xt.lower())) def update_member(self, member=None, resp=None, path=None, init=True): @@ -609,10 +603,7 @@ def _load( if resp.status != 200 and path.lower() == self.typepath.defs.biospath: raise BiosUnregisteredError() elif resp.status == 401: - raise SessionExpired( - "Invalid session. Please logout and " - "log back in or include credentials." - ) + raise SessionExpired("Invalid session. Please logout and " "log back in or include credentials.") elif resp.status not in (201, 200): self.removepath(path) return @@ -629,8 +620,7 @@ def _load( fpath = ( lambda pa, path: path - if pa.endswith(self.typepath.defs.hrefstring) - and pa.startswith((self.collstr, "Entries")) + if pa.endswith(self.typepath.defs.hrefstring) and pa.startswith((self.collstr, "Entries")) else None ) @@ -644,11 +634,7 @@ def _load( if "links" in resp.dict and "NextPage" in resp.dict["links"]: if originaluri: - next_link_uri = ( - originaluri - + "?page=" - + str(resp.dict["links"]["NextPage"]["page"]) - ) + next_link_uri = originaluri + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri self._load( @@ -661,9 +647,7 @@ def _load( loadcomplete=loadcomplete, ) else: - next_link_uri = ( - path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) - ) + next_link_uri = path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri self._load( @@ -678,9 +662,7 @@ def _load( # Only use monolith if we are set to matchrdirpath = ( - next( - (match for match in matches if match.value == self._resourcedir), None - ) + next((match for match in matches if match.value == self._resourcedir), None) if self.directory_load else None ) @@ -767,17 +749,13 @@ def _parse_schema(self, resp): if "redfish.dmtf.org" in jsonfile: if "odata" in jsonfile: - jsonpath = jsonpath.replace( - jsonpath.split("/")[-1], "odata" + jsonpath.split("/")[-1] - ) + jsonpath = jsonpath.replace(jsonpath.split("/")[-1], "odata" + jsonpath.split("/")[-1]) jsonfile = "Resource.json" found = re.search(typeregex, fullpath) if found: repitem = fullpath[found.regs[0][0] : found.regs[0][1]] - schemapath = "/" + fullpath.replace(repitem, "~").replace( - ".", "/" - ).replace("~", repitem) + schemapath = "/" + fullpath.replace(repitem, "~").replace(".", "/").replace("~", repitem) else: schemapath = "/" + fullpath.replace(".", "/") @@ -786,23 +764,11 @@ def _parse_schema(self, resp): if self.is_redfish: if resp.request.path[-1] == "/": - newpath = ( - "/".join(resp.request.path.split("/")[:-2]) - + "/" - + jsonfile - + "/" - ) + newpath = "/".join(resp.request.path.split("/")[:-2]) + "/" + jsonfile + "/" else: - newpath = ( - "/".join(resp.request.path.split("/")[:-1]) - + "/" - + jsonfile - + "/" - ) + newpath = "/".join(resp.request.path.split("/")[:-1]) + "/" + jsonfile + "/" else: - newpath = ( - "/".join(resp.request.path.split("/")[:-1]) + "/" + jsonfile - ) + newpath = "/".join(resp.request.path.split("/")[:-1]) + "/" + jsonfile if "href.json" in newpath: continue @@ -835,7 +801,7 @@ def _parse_schema(self, resp): try: # TODO may need to really verify this is acceptable regex listmatch = re.search("[][0-9]+[]", itempath) - except regexerr as excp: + except regexerr: pass # LOGGER.info("An error occurred with regex match on path: %s\n%s\n"\ # % (itempath, str(excp))) @@ -858,9 +824,7 @@ def _parse_schema(self, resp): if "$ref" in six.iterkeys(end.resolve(val)): end.resolve(val).pop("$ref") end.resolve(val).update(dictcopy) - replace_pointer = jsonpointer.JsonPointer( - end.path + jsonpath - ) + replace_pointer = jsonpointer.JsonPointer(end.path + jsonpath) data = replace_pointer.resolve(val) set_pointer(val, end.path, data) @@ -921,11 +885,7 @@ def _parse_schema_gen(self, resp): """ # pylint: disable=maybe-no-member - getval = ( - lambda inmat: getval(inmat.left) + "/" + str(inmat.right) - if hasattr(inmat, "left") - else str(inmat) - ) + getval = lambda inmat: getval(inmat.left) + "/" + str(inmat.right) if hasattr(inmat, "left") else str(inmat) respcopy = resp.dict jsonpath_expr = jsonpath_rw.parse('$.."anyOf"') while True: @@ -936,11 +896,7 @@ def _parse_schema_gen(self, resp): newval = None schlist = match.value schlist = [ele for ele in list(schlist) if ele != {"type": "null"}] - norefsch = [ - ele - for ele in list(schlist) - if isinstance(ele, dict) and len(ele.keys()) > 1 - ] + norefsch = [ele for ele in list(schlist) if isinstance(ele, dict) and len(ele.keys()) > 1] if norefsch: newval = norefsch[0] else: @@ -951,10 +907,7 @@ def _parse_schema_gen(self, resp): ele["$ref"] for ele in list(schlist) if "$ref" in ele.keys() - and ( - ele["$ref"].split("#")[0].endswith(".json") - and "odata" not in ele["$ref"].split("#")[0] - ) + and (ele["$ref"].split("#")[0].endswith(".json") and "odata" not in ele["$ref"].split("#")[0]) ] maxsch = max(schlist) newval = {"$ref": maxsch} @@ -973,9 +926,7 @@ def _parse_schema_gen(self, resp): if matches: for _, match in enumerate(matches): jsonfile = match.value.split("#")[0] - jsonfile = ( - "" if jsonfile.lower() == resp.request.path.lower() else jsonfile - ) + jsonfile = "" if jsonfile.lower() == resp.request.path.lower() else jsonfile jsonpath = match.value.split("#")[1] schemapath = "/" + getval(match.full_path) @@ -983,12 +934,7 @@ def _parse_schema_gen(self, resp): itempath = schemapath if "/" not in jsonfile: inds = -2 if resp.request.path[-1] == "/" else -1 - jsonfile = ( - "/".join(resp.request.path.split("/")[:inds]) - + "/" - + jsonfile - + "/" - ) + jsonfile = "/".join(resp.request.path.split("/")[:inds]) + "/" + jsonfile + "/" if jsonfile not in self.paths: self.load( jsonfile, @@ -1000,7 +946,7 @@ def _parse_schema_gen(self, resp): item = self.paths[jsonfile] if jsonfile in self.paths else None if not item: - if not "anyOf" in schemapath: + if "anyOf" not in schemapath: raise SchemaValidationError() continue if re.search("\[\d+]", itempath): @@ -1044,13 +990,9 @@ def load_from_dict(self, src): """ self._type = src["Type"] self._name = src["Name"] - self.typesadded = defaultdict( - set, {ki: set(val) for ki, val in src["typepath"].items()} - ) + self.typesadded = defaultdict(set, {ki: set(val) for ki, val in src["typepath"].items()}) self.ctree = defaultdict(set, {ki: set(val) for ki, val in src["ctree"].items()}) - self.colltypes = defaultdict( - set, {ki: set(val) for ki, val in src["colls"].items()} - ) + self.colltypes = defaultdict(set, {ki: set(val) for ki, val in src["colls"].items()}) for _, resp in list(src["resps"].items()): member = RisMonolithMemberv100(None, self.is_redfish) member.load_from_dict(resp) @@ -1081,9 +1023,7 @@ def markmodified(self, opath, path=None, modpaths=None): modpaths.update(self.ctree[path] if path in self.ctree else set()) self.paths[path].modified = True for npath in [ - unmodpath - for unmodpath in modpaths - if unmodpath in self.paths and not self.paths[unmodpath].modified + unmodpath for unmodpath in modpaths if unmodpath in self.paths and not self.paths[unmodpath].modified ]: self.markmodified(opath, path=npath, modpaths=modpaths) return modpaths @@ -1103,14 +1043,7 @@ def checkmodified(self, opath, path=None, modpaths=None): return if path in self.paths and self.paths[path].modified: newpaths = ( - set( - [ - conn - for conn in self.ctree[path] - if conn in self.paths and self.paths[path].modified - ] - ) - - modpaths + set([conn for conn in self.ctree[path] if conn in self.paths and self.paths[path].modified]) - modpaths ) modpaths.update(newpaths | set([path])) for npath in [unmodpath for unmodpath in newpaths]: @@ -1125,7 +1058,7 @@ def removepath(self, path): """ if path in self._visited_urls: self._visited_urls.remove(path) - if not path in self.paths: + if path not in self.paths: return if path in self.typesadded[self.paths[path].maj_type]: self.typesadded[self.paths[path].maj_type].remove(path) @@ -1134,41 +1067,28 @@ def removepath(self, path): del self.paths[path] if path in self.ctree: del self.ctree[path] - _ = [ - self.ctree[paths].remove(path) - for paths in self.ctree - if path in self.ctree[paths] - ] + _ = [self.ctree[paths].remove(path) for paths in self.ctree if path in self.ctree[paths]] def _populatecollections(self): """Populate the collections type and types depending on resourcedirectory""" - if not self._resourcedir in self.paths: + if self._resourcedir not in self.paths: return self.colltypes = defaultdict(set) alltypes = [] colls = [] for item in self.paths[self._resourcedir].dict["Instances"]: # Fix for incorrect RDir instances. - if ( - not self.typepath.defs.typestring in item - or item[self.typepath.defs.hrefstring] in self.paths - ): + if self.typepath.defs.typestring not in item or item[self.typepath.defs.hrefstring] in self.paths: continue - typename = ".".join( - item[self.typepath.defs.typestring].split(".", 2)[:2] - ).split("#")[-1] - _ = [alltypes.append(typename) if not "Collection" in typename else None] + typename = ".".join(item[self.typepath.defs.typestring].split(".", 2)[:2]).split("#")[-1] + _ = [alltypes.append(typename) if "Collection" not in typename else None] _ = [colls.append(typename) if "Collection" in typename else None] member = RisMonolithMemberv100(None, self.is_redfish) - member.popdefs( - typename, item[self.typepath.defs.hrefstring], item[self.etagstr] - ) + member.popdefs(typename, item[self.typepath.defs.hrefstring], item[self.etagstr]) self.update_member(member=member, init=False) for coll in colls: collname = coll.split("Collection")[0].split("#")[-1] - typename = next( - (name for name in alltypes if name.startswith(collname)), None - ) + typename = next((name for name in alltypes if name.startswith(collname)), None) colltype = ".".join(coll.split(".", 2)[:2]).split("#")[-1] self.colltypes[typename].add(colltype) @@ -1180,16 +1100,12 @@ def capture(self, redmono=False): :type redmono: bool :rtype: dict """ - self.load( - includelogs=True, crawl=True, loadcomplete=True, path_refresh=True, init=True - ) + self.load(includelogs=True, crawl=True, loadcomplete=True, path_refresh=True, init=True) return ( self.to_dict() if not redmono else { - x: {"Headers": v.resp.getheaders(), "Response": v.resp.dict} - for x, v in list(self.paths.items()) - if v + x: {"Headers": v.resp.getheaders(), "Response": v.resp.dict} for x, v in list(self.paths.items()) if v } ) diff --git a/src/redfish/ris/ris_threaded.py b/src/redfish/ris/ris_threaded.py index fbb5886..13274d3 100644 --- a/src/redfish/ris/ris_threaded.py +++ b/src/redfish/ris/ris_threaded.py @@ -19,15 +19,14 @@ import logging import threading +from queue import Empty + +import jsonpath_rw # Added for py3 compatibility import six - -from queue import Empty from six.moves.urllib.parse import urlparse, urlunparse -import jsonpath_rw - import redfish.ris # ---------End of imports--------- @@ -110,8 +109,7 @@ def run(self): elif resp.status == 401: self.queue.task_done() raise redfish.ris.ris.SessionExpired( - "Invalid session. Please logout and " - "log back in or include credentials." + "Invalid session. Please logout and " "log back in or include credentials." ) elif resp.status not in (201, 200): theobj.removepath(path) @@ -122,8 +120,7 @@ def run(self): fpath = ( lambda pa, path: path - if pa.endswith(theobj.typepath.defs.hrefstring) - and pa.startswith((theobj.collstr, "Entries")) + if pa.endswith(theobj.typepath.defs.hrefstring) and pa.startswith((theobj.collstr, "Entries")) else None ) @@ -136,11 +133,7 @@ def run(self): if "links" in resp.dict and "NextPage" in resp.dict["links"]: if originaluri: - next_link_uri = ( - originaluri - + "?page=" - + str(resp.dict["links"]["NextPage"]["page"]) - ) + next_link_uri = originaluri + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri theobj.get_queue.put( @@ -157,9 +150,7 @@ def run(self): ) ) else: - next_link_uri = ( - path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) - ) + next_link_uri = path + "?page=" + str(resp.dict["links"]["NextPage"]["page"]) href = "%s" % next_link_uri theobj.get_queue.put( @@ -179,11 +170,7 @@ def run(self): # Only use monolith if we are set to matchrdirpath = ( next( - ( - match - for match in matches - if match.value == theobj._resourcedir - ), + (match for match in matches if match.value == theobj._resourcedir), None, ) if theobj.directory_load diff --git a/src/redfish/ris/rmc.py b/src/redfish/ris/rmc.py index aad8583..6c6d359 100644 --- a/src/redfish/ris/rmc.py +++ b/src/redfish/ris/rmc.py @@ -17,13 +17,15 @@ """A convenience layer that combines multiple lower level classes and functions into one.""" +import copy +import hashlib +import logging +import shutil + # ---------Imports--------- import sys import time -import copy -import shutil -import logging -import hashlib + import six try: @@ -33,38 +35,38 @@ import jsonpatch import jsonpointer + import redfish.ris.gen_compat import redfish.ris.validation - from redfish.rest.v1 import RestClient -from redfish.ris.ris import SessionExpired, RisMonolith, SchemaValidationError -from redfish.ris.validation import ValidationManager, Typepathforval from redfish.ris.resp_handler import ResponseHandler -from redfish.ris.utils import ( - print_handler, - merge_dict, - getattributeregistry, - diffdict, - navigatejson, - iterateandclear, - skipnonsettingsinst, - warning_handler, - validate_headers, - checkallowablevalues, -) +from redfish.ris.ris import RisMonolith, SchemaValidationError, SessionExpired from redfish.ris.rmc_helper import ( - UndefinedClientError, + EmptyRaiseForEAFP, + IloResponseError, + IncompatibleiLOVersionError, InstanceNotFoundError, + LoadSkipSettingError, NothingSelectedError, - ValidationError, - RmcFileCacheManager, NothingSelectedSetError, - LoadSkipSettingError, + RmcFileCacheManager, + UndefinedClientError, + ValidationError, ValueChangedError, - IloResponseError, - EmptyRaiseForEAFP, - IncompatibleiLOVersionError, ) +from redfish.ris.utils import ( + checkallowablevalues, + diffdict, + getattributeregistry, + iterateandclear, + merge_dict, + navigatejson, + print_handler, + skipnonsettingsinst, + validate_headers, + warning_handler, +) +from redfish.ris.validation import Typepathforval, ValidationManager # ---------End of imports--------- @@ -132,9 +134,7 @@ def validationmanager(self): self._validationmanager.reset_errors_warnings() else: monolith = self.monolith - self._validationmanager = ValidationManager( - monolith, defines=self.typepath - ) + self._validationmanager = ValidationManager(monolith, defines=self.typepath) self._validationmanager.updatevalidationdata() else: self._validationmanager = None @@ -216,6 +216,7 @@ def login( ssl_cert=None, user_ca_cert_data=None, json_out=False, + login_otp=None, ): """Performs a login on a the server specified by the keyword arguments. Will also create a monolith, client, and update the compatibility classes for the app instance. If base_url @@ -259,11 +260,11 @@ def login( ca_cert_data=user_ca_cert_data, proxy=proxy, isredfish=is_redfish, + login_otp=login_otp, ) if user_ca_cert_data and self.typepath.iloversion < 5.23: raise IncompatibleiLOVersionError( - "Certificate based login is incompatible with this " - "iLO version: %s\n" % self.typepath.iloversion + "Certificate based login is incompatible with this " "iLO version: %s\n" % self.typepath.iloversion ) is_redfish = self.typepath.updatedefinesflag(redfishflag=is_redfish) @@ -280,15 +281,14 @@ def login( is_redfish=is_redfish, proxy=proxy, ca_cert_data=user_ca_cert_data, + login_otp=login_otp, ) self.current_client.login(self.current_client.auth_type) inittime = time.time() - self._build_monolith( - path=path, includelogs=includelogs, skipbuild=skipbuild, json_out=json_out - ) + self._build_monolith(path=path, includelogs=includelogs, skipbuild=skipbuild, json_out=json_out) endtime = time.time() if self.verbose > 1: @@ -361,16 +361,11 @@ def select(self, selector=None, fltrvals=(None, None), path_refresh=False): raise NothingSelectedError() selector = self.typepath.modifyselectorforgen(selector) instances = self._getinstances(selector=selector, path_refresh=path_refresh) - val = ( - fltrvals[1].strip("'\"") - if isinstance(fltrvals[1], six.string_types) - else fltrvals[1] - ) + val = fltrvals[1].strip("'\"") if isinstance(fltrvals[1], six.string_types) else fltrvals[1] instances = [ inst for inst in instances - if not fltrvals[0] - or navigatejson(fltrvals[0].split("/"), copy.deepcopy(inst.dict), val) + if not fltrvals[0] or navigatejson(fltrvals[0].split("/"), copy.deepcopy(inst.dict), val) ] if any(instances): self.selector = selector @@ -378,9 +373,7 @@ def select(self, selector=None, fltrvals=(None, None), path_refresh=False): return instances errmsg = ( - "Unable to locate instance for '{0}' and filter '{1}={2}'".format( - selector, fltrvals[0], fltrvals[1] - ) + "Unable to locate instance for '{0}' and filter '{1}={2}'".format(selector, fltrvals[0], fltrvals[1]) if fltrvals[0] and fltrvals[1] else "Unable to locate instance for {}\n".format(selector) ) @@ -400,19 +393,11 @@ def types(self, fulltypes=False): if self.monolith: monolith = self.monolith - rdirtype = next( - monolith.gettypename(self.typepath.defs.resourcedirectorytype), None - ) + rdirtype = next(monolith.gettypename(self.typepath.defs.resourcedirectorytype), None) if not rdirtype: for inst in monolith.iter(): - if not any( - [ - x - for x in ["ExtendedError", "object", "string"] - if x in inst.type - ] - ): + if not any([x for x in ["ExtendedError", "object", "string"] if x in inst.type]): instances.append(inst.type) else: for instance in monolith.iter(rdirtype): @@ -420,7 +405,7 @@ def types(self, fulltypes=False): if ( item and instance._typestring in list(item.keys()) - and not "ExtendedError" in item[instance._typestring] + and "ExtendedError" not in item[instance._typestring] ): if not fulltypes and instance._typestring == "@odata.type": tval = item["@odata.type"].split("#") @@ -540,16 +525,10 @@ def info(self, selector=None, props=None, dumpjson=True, latestschema=False): for inst in instances: bsmodel = None currdict = inst.resp.dict - proppath = ( - inst.resp.getheader("Link").split(";")[0].strip("<>") - if inst.resp.getheader("Link") - else None - ) + proppath = inst.resp.getheader("Link").split(";")[0].strip("<>") if inst.resp.getheader("Link") else None seldict = {} if not props: - model, bsmodel = self.get_model( - currdict, attributeregistry, latestschema, proppath=proppath - ) + model, bsmodel = self.get_model(currdict, attributeregistry, latestschema, proppath=proppath) results = model break if isinstance(props, six.string_types): @@ -571,19 +550,12 @@ def info(self, selector=None, props=None, dumpjson=True, latestschema=False): if not model and not bsmodel: errmsg = "/".join(props) warning_handler( - "Unable to locate registry model or " - "No data available for entry: {}\n".format(errmsg) + "Unable to locate registry model or " "No data available for entry: {}\n".format(errmsg) ) continue found = model.get_validator(props[-1]) if model else None found = bsmodel.get_validator(props[-1]) if not found and bsmodel else found - outdata = ( - found - if found and dumpjson - else found.print_help(props[-1]) - if found - else outdata - ) + outdata = found if found and dumpjson else found.print_help(props[-1]) if found else outdata if outdata or results: return outdata if outdata else results @@ -681,17 +653,11 @@ def loadset( if patches: torem = [] - _ = [ - torem.append(patch) - for patch in patches.patch - if patch["op"] == "remove" - ] + _ = [torem.append(patch) for patch in patches.patch if patch["op"] == "remove"] _ = [patches.patch.remove(patch) for patch in torem] for ind, item in enumerate(instance.patches): - ppath = ( - item.patch[0]["path"] if hasattr(item, "patch") else item[0]["path"] - ) + ppath = item.patch[0]["path"] if hasattr(item, "patch") else item[0]["path"] # ppath = ["path"](getattr(item, "patch"), item)[0]["path"] jpath = jsonpointer.JsonPointer(ppath.lower()) jval = jpath.resolve(seldict, default="kasjdk?!") @@ -700,11 +666,7 @@ def loadset( if patches: for patch in patches.patch: - forprint = ( - patch["value"] - if "value" in patch - else (patch["op"] + " " + patch["from"]) - ) + forprint = patch["value"] if "value" in patch else (patch["op"] + " " + patch["from"]) results.append({patch["path"][1:]: forprint}) if "Managers/1/EthernetInterfaces/1" not in instance.path: self.monolith.path(instance.path).patches.append(patches) @@ -748,16 +710,12 @@ def status(self): prop = item["path"][1:].split("/")[-1] validator = bsmodel.get_validator(prop) if validator: - if isinstance( - validator, redfish.ris.validation.PasswordValidator - ): + if isinstance(validator, redfish.ris.validation.PasswordValidator): item["value"] = "******" itemholder.append(item) if itemholder: - finalresults.append( - {instance.maj_type + "(" + instance.path + ")": itemholder} - ) + finalresults.append({instance.maj_type + "(" + instance.path + ")": itemholder}) return finalresults @@ -771,9 +729,7 @@ def commit(self): """ # Protect iLO Network Interface changes. instances = [ - inst - for inst in self.monolith.iter() - if inst.patches and "Managers/1/EthernetInterfaces/1" not in inst.path + inst for inst in self.monolith.iter() if inst.patches and "Managers/1/EthernetInterfaces/1" not in inst.path ] if not instances or len(instances) == 0: @@ -828,10 +784,7 @@ def commit(self): hash = hashlib.sha256(value.encode()).hexdigest().upper() headers = dict([("X-HPRESTFULAPI-AuthToken", hash)]) elif key == "AdminPassword" and ( - (value == None) - or (value == "none") - or (value == "None") - or (value == "null") + (value is None) or (value == "none") or (value == "None") or (value == "null") ): currdict["AdminPassword"] = "" try: @@ -893,9 +846,9 @@ def patch_handler( self._updatemono(path=put_path, path_refresh=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) return results @@ -951,7 +904,9 @@ def get_handler( if results and results.status == 200 and sessionid: if not silent: if hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) else: print_handler("[" + str(results.status) + "]" + " The operation completed successfully.\n") return results @@ -965,9 +920,9 @@ def get_handler( if not silent: if hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) else: print_handler("[" + str(results.status) + "]" + " The operation completed successfully.\n") @@ -1003,9 +958,9 @@ def post_handler(self, put_path, body, headers=None, silent=False, service=False self._modifiedpath(results) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) return results @@ -1048,9 +1003,9 @@ def put_handler( self._modifiedpath(results, replace=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) return results @@ -1079,9 +1034,9 @@ def delete_handler(self, put_path, headers=None, silent=False, service=False): self._modifiedpath(results, delete=True) if not silent and hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) return results @@ -1107,15 +1062,13 @@ def head_handler(self, put_path, silent=False, service=False): raise SessionExpired() if not silent and hasattr(self.typepath.defs, "messageregistrytype"): - ResponseHandler( - self.validationmanager, self.typepath.defs.messageregistrytype - ).output_resp(results, dl_reg=service, verbosity=self.verbose) + ResponseHandler(self.validationmanager, self.typepath.defs.messageregistrytype).output_resp( + results, dl_reg=service, verbosity=self.verbose + ) return results - def removereadonlyprops( - self, currdict, emptyraise=False, removeunique=True, specify_props=None - ): + def removereadonlyprops(self, currdict, emptyraise=False, removeunique=True, specify_props=None): """Remove read only properties from a dictionary. Requires schemas to be available. :param currdict: The dictionary to remove read only properties from. @@ -1206,11 +1159,7 @@ def getcollectionmembers(self, path, fullresp=False): members = self.get_handler(path, service=True, silent=True) if members and not fullresp: try: - members = ( - members.dict["Members"] - if self.typepath.defs.isgen10 - else members.dict["Current"] - ) + members = members.dict["Members"] if self.typepath.defs.isgen10 else members.dict["Current"] except KeyError: members = members elif fullresp: @@ -1244,14 +1193,10 @@ def getiloversion(self, skipschemas=False): :type skipschemas: bool :returns: returns current iLO version """ - iloversion = self._iloversion = ( - self._iloversion if self._iloversion else self.typepath.iloversion - ) + iloversion = self._iloversion = self._iloversion if self._iloversion else self.typepath.iloversion if not iloversion and hasattr(self.redfishinst, "iloversion"): - iloversion = ( - self._iloversion - ) = self.typepath.iloversion = self.redfishinst.iloversion + iloversion = self._iloversion = self.typepath.iloversion = self.redfishinst.iloversion if ( hasattr(self.typepath, "gencompany") @@ -1260,9 +1205,7 @@ def getiloversion(self, skipschemas=False): and not self.typepath.noschemas ): self.monolith.load(self.typepath.defs.managerpath, crawl=False) - results = next( - iter(self.getprops("Manager.", ["FirmwareVersion", "Firmware"])) - ) + results = next(iter(self.getprops("Manager.", ["FirmwareVersion", "Firmware"]))) def quickdrill(_dict, key): """function to find key in nested dictionary""" @@ -1281,8 +1224,7 @@ def quickdrill(_dict, key): self._iloversion = iloversion elif ( - hasattr(self.typepath, "gencompany") and - not self.typepath.gencompany + hasattr(self.typepath, "gencompany") and not self.typepath.gencompany ): # Assume schemas are available somewhere in non-hpe redfish self._iloversion = iloversion = 4.210 @@ -1291,13 +1233,9 @@ def quickdrill(_dict, key): if iloversion and iloversion >= 4.210: conf = self._verifyschemasdownloaded(self.monolith) elif iloversion and iloversion < 4.210: - warning_handler( - "Please upgrade to iLO 4 version 2.1 or above for schema support." - ) + warning_handler("Please upgrade to iLO 4 version 2.1 or above for schema support.") else: - warning_handler( - "Schema support unavailable on the currently logged in system." - ) + warning_handler("Schema support unavailable on the currently logged in system.") return iloversion if iloversion and iloversion >= 4.210 and conf else None @@ -1344,9 +1282,7 @@ def create_save_header(self): instances["Comments"]["Model"] = instance.resp.obj["Model"] try: if instance.resp.obj["Oem"][self.typepath.defs.oemhp]["Bios"]["Current"]: - oemjson = instance.resp.obj["Oem"][self.typepath.defs.oemhp]["Bios"][ - "Current" - ] + oemjson = instance.resp.obj["Oem"][self.typepath.defs.oemhp]["Bios"]["Current"] instances["Comments"]["BIOSFamily"] = oemjson["Family"] instances["Comments"]["BIOSDate"] = oemjson["Date"] except KeyError: @@ -1355,14 +1291,10 @@ def create_save_header(self): try: if getattr(instance.resp.obj, "Attributes", False): if instance.resp.obj["Attributes"].get("SerialNumber"): - instances["Comments"]["SerialNumber"] = instance.resp.obj[ - "Attributes" - ]["SerialNumber"] + instances["Comments"]["SerialNumber"] = instance.resp.obj["Attributes"]["SerialNumber"] if instance.resp.obj.get("SerialNumber"): - instances["Comments"]["SerialNumber"] = instance.resp.obj[ - "SerialNumber" - ] - except KeyError as e: + instances["Comments"]["SerialNumber"] = instance.resp.obj["SerialNumber"] + except KeyError: pass for instance in monolith.iter("Manager."): if instance.resp.obj.get("FirmwareVersion"): @@ -1404,9 +1336,7 @@ def download_path(self, paths, crawl=True, path_refresh=False): else: raise excp - def get_model( - self, currdict, attributeregistry, latestschema=None, newarg=None, proppath=None - ): + def get_model(self, currdict, attributeregistry, latestschema=None, newarg=None, proppath=None): """Returns a model and possibly a bios model for the current instance's schema/registry. This model can be used to read schema data and validate patches. @@ -1436,16 +1366,10 @@ def get_model( if not attributeregistry and model: return model, bsmodel if not model and not attributeregistry: - LOGGER.warning( - "Unable to locate registry/schema for %s\n", currdict[type_str] - ) + LOGGER.warning("Unable to locate registry/schema for %s\n", currdict[type_str]) return None, None attrval = currdict.get("AttributeRegistry", None) - attrval = ( - list(attributeregistry.values())[0] - if not attrval and attributeregistry - else attrval - ) + attrval = list(attributeregistry.values())[0] if not attrval and attributeregistry else attrval bsmodel = valobj.get_registry_model( currtype=attrval if attrval else currdict[type_str], newarg=newarg, @@ -1454,9 +1378,7 @@ def get_model( ) return model, bsmodel - def _build_monolith( - self, path=None, includelogs=False, skipbuild=False, json_out=False - ): + def _build_monolith(self, path=None, includelogs=False, skipbuild=False, json_out=False): """Run through the RIS tree to build monolith :param path: path to initiate login to. @@ -1468,9 +1390,7 @@ def _build_monolith( """ self.monolith = RisMonolith(self.current_client, self.typepath) if not skipbuild: - self.monolith.load( - path=path, includelogs=includelogs, init=True, json_out=json_out - ) + self.monolith.load(path=path, includelogs=includelogs, init=True, json_out=json_out) else: self.monolith.update_member( resp=self.current_client.root, @@ -1488,7 +1408,7 @@ def _modifiedpath(self, results, delete=False, replace=False): :param results: Response for the path :type results: RestResponse """ - if not results or not results.status in (200, 201): + if not results or results.status not in (200, 201): return path = results.path path = path.split("/Actions")[0] if "Actions" in path else path @@ -1511,22 +1431,17 @@ def _checkforchange(self, paths, crawl=True): (pathtoetag, _) = self._gettypeswithetag() mono = self.monolith self.download_path(list(paths), crawl=crawl, path_refresh=True) - etags = [ - None if not path in mono.paths else mono.paths[path].etag for path in paths - ] + etags = [None if path not in mono.paths else mono.paths[path].etag for path in paths] sametag = [ path for ind, path in enumerate(paths) - if path in pathtoetag - and path in self.monolith.paths - and pathtoetag[path] != etags[ind] + if path in pathtoetag and path in self.monolith.paths and pathtoetag[path] != etags[ind] ] for path in sametag: self.monolith.paths[path].patches = [] if sametag: LOGGER.warning( - "The data in the following paths have been updated. " - "Recheck the changes made to . %s", + "The data in the following paths have been updated. " "Recheck the changes made to . %s", ",".join([str(path) for path in sametag]), ) @@ -1557,9 +1472,7 @@ def _updatemono(self, currtype=None, path=None, crawl=False, path_refresh=False) paths.add(path) if resp.modified: paths.add(path) - paths.update( - monolith.checkmodified(path) if path in monolith.ctree else set() - ) + paths.update(monolith.checkmodified(path) if path in monolith.ctree else set()) elif path: if monolith.paths and not list(monolith.paths)[0][-1] == "/": path = path[:-1] if path[-1] == "/" else path @@ -1567,9 +1480,7 @@ def _updatemono(self, currtype=None, path=None, crawl=False, path_refresh=False) paths.add(path) if path in monolith.paths and monolith.paths[path].modified: paths.add(path) - paths.update( - monolith.checkmodified(path) if path in monolith.ctree else set() - ) + paths.update(monolith.checkmodified(path) if path in monolith.ctree else set()) if paths: self._checkforchange(list(paths), crawl=crawl) @@ -1587,33 +1498,22 @@ def _verifyschemasdownloaded(self, monolith): warning_handler("Missing Schemas or registries.") return None - schemacoll = next( - monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None - ) + schemacoll = next(monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None) if not schemacoll or any( - paths.lower() == schemaid and monolith.paths[paths] - for paths in monolith.typesadded[schemacoll] + paths.lower() == schemaid and monolith.paths[paths] for paths in monolith.typesadded[schemacoll] ): self.download_path([schemaid], crawl=False) - schemacoll = next( - monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None - ) + schemacoll = next(monolith.gettypename(self.typepath.defs.schemafilecollectiontype), None) - regcoll = next( - monolith.gettypename(self.typepath.defs.regfilecollectiontype), None - ) + regcoll = next(monolith.gettypename(self.typepath.defs.regfilecollectiontype), None) if not regcoll or any( - paths.lower() == regid and monolith.paths[paths] - for paths in monolith.typesadded[regcoll] + paths.lower() == regid and monolith.paths[paths] for paths in monolith.typesadded[regcoll] ): self.download_path([regid], crawl=False) - regcoll = next( - monolith.gettypename(self.typepath.defs.regfilecollectiontype), None - ) + regcoll = next(monolith.gettypename(self.typepath.defs.regfilecollectiontype), None) return any( - paths.lower() in (schemaid.lower(), regid.lower()) and monolith.paths[paths] - for paths in monolith.paths + paths.lower() in (schemaid.lower(), regid.lower()) and monolith.paths[paths] for paths in monolith.paths ) def _validatechanges( @@ -1646,9 +1546,7 @@ def _validatechanges( validation_manager = self.validationmanager errors, warnings = validation_manager.validatedict( newdict, - currtype=attributeregistry[instance.maj_type] - if attributeregistry - else currtype, + currtype=attributeregistry[instance.maj_type] if attributeregistry else currtype, monolith=entrymono, unique=unique, searchtype=self.typepath.defs.attributeregtype if attributeregistry else None, @@ -1681,9 +1579,7 @@ def _getinstances(self, selector=None, path_refresh=False, crawl=False): if selector: selector = ".".join(selector.split("#")[-1].split(".")[:2]) if self.monolith: - self._updatemono( - currtype=selector, crawl=crawl, path_refresh=path_refresh - ) + self._updatemono(currtype=selector, crawl=crawl, path_refresh=path_refresh) if not selector: return instances selector = None if selector == '"*"' else selector @@ -1692,8 +1588,7 @@ def _getinstances(self, selector=None, path_refresh=False, crawl=False): instances = [ inst for inst in self.monolith.iter(selector) - if inst.maj_type not in ["object", "string"] - and "redfish" in inst.path + if inst.maj_type not in ["object", "string"] and "redfish" in inst.path ] else: instances = [ @@ -1748,9 +1643,7 @@ def _checkpostpatch(self, body=None, path=None, patch=False): if results and results.status == 200: if results.dict: if "Target" in body: - actions = results.dict["Oem"][self.typepath.defs.oemhp][ - "Actions" - ] + actions = results.dict["Oem"][self.typepath.defs.oemhp]["Actions"] elif "Actions" in body: actions = results.dict["Actions"] else: @@ -1787,9 +1680,7 @@ def _checkforetagchange(self, instance=None): (oldtag, _) = self._gettypeswithetag() self._updatemono(path=path, path_refresh=True) (newtag, _) = self._gettypeswithetag() - if ( - oldtag[path] != newtag[path] - ) and not self.typepath.defs.hpilodatetimetype in instance.maj_type: + if (oldtag[path] != newtag[path]) and self.typepath.defs.hpilodatetimetype not in instance.maj_type: warning_handler( "The property you are trying to change " "has been updated. Please check entry again " diff --git a/src/redfish/ris/rmc_helper.py b/src/redfish/ris/rmc_helper.py index 84b65c4..8143dbb 100644 --- a/src/redfish/ris/rmc_helper.py +++ b/src/redfish/ris/rmc_helper.py @@ -19,14 +19,14 @@ # ---------Imports--------- -import os -import json import errno -import logging import hashlib +import json +import logging +import os +from redfish.rest.containers import RestRequest, StaticRestResponse from redfish.rest.v1 import RestClient -from redfish.rest.containers import StaticRestResponse, RestRequest from .ris import RisMonolith from .sharedtypes import JSONEncoder @@ -55,6 +55,10 @@ class InvalidCommandLineError(RdmcError): pass +class TfaEnablePreRequisiteError(RdmcError): + """Raised when pre-requisites not met while enabling TFA""" + + pass class FailureDuringCommitError(RdmcError): """Raised when there is an error while committing.""" @@ -91,6 +95,7 @@ class CurrentlyLoggedInError(Exception): pass + class IloLicenseError(Exception): """Raised when the proper iLO license is not available for a command""" @@ -102,6 +107,7 @@ class ScepenabledError(Exception): pass + class NothingSelectedError(Exception): """Raised when attempting to access an object without first selecting it.""" @@ -156,12 +162,6 @@ class UnableToObtainIloVersionError(Exception): pass -class IncompatibleiLOVersionError(Exception): - """Raised when the iLO version is above or below the required version.""" - - pass - - class ValidationError(Exception): """Raised when there is a problem with user input.""" @@ -253,29 +253,20 @@ def logout_del_function(self, url=None): data = json.load(monolith) monolith.close() for item in data: - if ( - "login" in item - and "session_location" in data["login"] - ): + if "login" in item and "session_location" in data["login"]: if "blobstore" in data["login"]["url"]: - loc = data["login"]["session_location"].split( - "//" - )[-1] + loc = data["login"]["session_location"].split("//")[-1] sesurl = None else: loc = None if data["login"]["session_location"] is not None: - loc = data["login"]["session_location"].split( - data["login"]["url"] - )[-1] + loc = data["login"]["session_location"].split(data["login"]["url"])[-1] sesurl = data["login"]["url"] sessionlocs.append( ( loc, sesurl, - self._rmc._cm.decodefunct( - data["login"]["session_key"] - ), + self._rmc._cm.decodefunct(data["login"]["session_key"]), ) ) @@ -364,9 +355,7 @@ def _uncache_client(self, cachefn, creds=None, enc=False): if login_data.get("authorization_key"): redfishinst.basic_auth = login_data.get("authorization_key") elif login_data.get("session_key"): - redfishinst.session_key = self._rmc._cm.decodefunct( - login_data.get("session_key") - ) + redfishinst.session_key = self._rmc._cm.decodefunct(login_data.get("session_key")) if isinstance(redfishinst.session_key, bytes): redfishinst.session_key = redfishinst.session_key.decode("utf-8") redfishinst.session_location = login_data.get("session_location") @@ -455,9 +444,7 @@ def cache_rmc(self): get=self._rmc.monolith.paths, ) - clientsfh = open( - "%s/%s" % (cachedir, index_map[self._rmc.redfishinst.base_url]), "w" - ) + clientsfh = open("%s/%s" % (cachedir, index_map[self._rmc.redfishinst.base_url]), "w") if isinstance(clients_data, bytes): clients_data = clients_data.decode("utf-8") diff --git a/src/redfish/ris/sharedtypes.py b/src/redfish/ris/sharedtypes.py index 2894723..845286d 100644 --- a/src/redfish/ris/sharedtypes.py +++ b/src/redfish/ris/sharedtypes.py @@ -20,7 +20,9 @@ # ---------Imports--------- import logging + import jsonpatch + from redfish.rest.containers import JSONEncoder # ---------End of imports--------- diff --git a/src/redfish/ris/utils.py b/src/redfish/ris/utils.py index a1723b9..64de1f7 100644 --- a/src/redfish/ris/utils.py +++ b/src/redfish/ris/utils.py @@ -16,11 +16,12 @@ # -*- coding: utf-8 -*- """Utility functions for internal and external use. Contains general json navigating functions as well as some monolith utility functions.""" +import copy +import logging import re import sys + import six -import copy -import logging if six.PY3: from functools import reduce @@ -31,7 +32,6 @@ from collections.abc import Mapping import jsonpath_rw - from six import iterkeys, string_types from redfish.ris.rmc_helper import IncorrectPropValue @@ -58,11 +58,12 @@ def print_handler(msg): :param msg: The warning message. :type msg: str """ - #if override: + # if override: sys.stdout.write(msg) - #else: - #if LOGGER.getEffectiveLevel() == 40: - #LOGGER.warning(msg) + # else: + # if LOGGER.getEffectiveLevel() == 40: + # LOGGER.warning(msg) + def warning_handler(msg, override=False): """Helper function for handling warning messages appropriately. If LOGGER level is set to 40 @@ -71,10 +72,10 @@ def warning_handler(msg, override=False): :param msg: The warning message. :type msg: str """ - #if override: - #sys.stdout.write(msg) - #else: - #if LOGGER.getEffectiveLevel() == 40: + # if override: + # sys.stdout.write(msg) + # else: + # if LOGGER.getEffectiveLevel() == 40: LOGGER.warning(msg) @@ -92,11 +93,9 @@ def validate_headers(instance, verbose=False): headervals = instance.resp.getheaders() for kii, val in headervals.items(): if kii.lower() == "allow": - if not "PATCH" in val: + if "PATCH" not in val: if verbose: - warning_handler( - "Skipping read-only path: %s\n" % instance.resp.request.path - ) + warning_handler("Skipping read-only path: %s\n" % instance.resp.request.path) skip = True except: pass @@ -206,16 +205,12 @@ def checkallowablevalues(newdict=None, oridict=None): valmatches = jexpr2.find(newdict) if valmatches: for mat in valmatches: - res = [ - val for val in match.value if mat.value.lower() == val.lower() - ] + res = [val for val in match.value if mat.value.lower() == val.lower()] if not res: raise IncorrectPropValue( "Incorrect Value " "entered. Please enter one of the below " - "values for {0}:\n{1}".format( - "/".join(checkpath.split(".")), str(match.value)[1:-1] - ) + "values for {0}:\n{1}".format("/".join(checkpath.split(".")), str(match.value)[1:-1]) ) @@ -234,12 +229,8 @@ def navigatejson(selector, currdict, val=None): # TODO: Check for val of different types(bool, int, etc) temp_dict = dict() createdict = lambda y, x: {x: y} - getkey = lambda cdict, sel: next( - (item for item in iterkeys(cdict) if sel.lower() == item.lower()), sel - ) - getval = lambda cdict, sele: [ - cdict[sel] if sel in cdict else "~!@#$%^&*)()" for sel in [getkey(cdict, sele)] - ][0] + getkey = lambda cdict, sel: next((item for item in iterkeys(cdict) if sel.lower() == item.lower()), sel) + getval = lambda cdict, sele: [cdict[sel] if sel in cdict else "~!@#$%^&*)()" for sel in [getkey(cdict, sele)]][0] fullbreak = False seldict = copy.deepcopy(currdict) for ind, sel in enumerate(selector): @@ -249,9 +240,7 @@ def navigatejson(selector, currdict, val=None): if seldict == "~!@#$%^&*)()": return None if val and ind == len(selector) - 1: - cval = ( - ",".join(seldict) if isinstance(seldict, (list, tuple)) else seldict - ) + cval = ",".join(seldict) if isinstance(seldict, (list, tuple)) else seldict if not ( (val[-1] == "*" and str(cval).lower().startswith(val[:-1].lower())) or str(cval).lower() == val.lower() @@ -348,11 +337,8 @@ def getattributeregistry(instances, adict=None): newdict[inst.maj_type] = inst.resp.obj["AttributeRegistry"] return newdict newdict[inst.maj_type] = inst.resp.obj["AttributeRegistry"] - except AttributeError as excp: - LOGGER.warning( - "Invalid/Unpopulated Response: %s\nType:%s\nPath:%s\n" - % (inst.resp, inst.type, inst.path) - ) + except AttributeError: + LOGGER.warning("Invalid/Unpopulated Response: %s\nType:%s\nPath:%s\n" % (inst.resp, inst.type, inst.path)) return newdict @@ -418,9 +404,7 @@ def diffdict(newdict=None, oridict=None, settingskipped=[False]): continue else: if val: - if [va.lower() for va in val] == [ - va.lower() if va else va for va in oridict[key] - ]: + if [va.lower() for va in val] == [va.lower() if va else va for va in oridict[key]]: del newdict[key] # TODO: check if lowercase is correct or buggy for string types elif isinstance(val, (string_types, int, type(None))): @@ -459,7 +443,7 @@ def json_traversal(data, key_to_find, ret_dict=False): try: if _iter == data: return None - except Exception as exp: + except Exception: pass try: if key_to_find.lower() == _iter.lower(): @@ -467,7 +451,7 @@ def json_traversal(data, key_to_find, ret_dict=False): return data else: return data[_iter] - except Exception as exp: + except Exception: pass try: if key_to_find.lower() in [str(_.lower()) for _ in _iter.keys()]: @@ -475,7 +459,7 @@ def json_traversal(data, key_to_find, ret_dict=False): return data else: return data[_iter] - except Exception as exp: + except Exception: pass _tmp = None try: @@ -490,14 +474,14 @@ def json_traversal(data, key_to_find, ret_dict=False): elif isinstance(data[_iter], list) or isinstance(data[_iter], tuple): try: _tmp = json_traversal(data[i], key_to_find, ret_dict) - except Exception as exp: + except Exception: _tmp = json_traversal(data[_iter], key_to_find, ret_dict) - except Exception as exp: + except Exception: _tmp = json_traversal(data[i], key_to_find, ret_dict) finally: if _tmp: return _tmp - except Exception as exp: + except Exception: pass @@ -552,11 +536,7 @@ def json_traversal_delete_empty(data, old_key=None, _iter=None, remove_list=None # would be great to not need this section; however, # since recursive deletion is not possible, this is needed # if you can figure out how to pass by reference then fix me! - if ( - (isinstance(value, dict) and len(value) < 1) - or None - or value in remove_list - ): + if (isinstance(value, dict) and len(value) < 1) or None or value in remove_list: delete_list.append(key) for dl_entry in delete_list: try: diff --git a/src/redfish/ris/validation.py b/src/redfish/ris/validation.py index 17fcb08..81dee25 100644 --- a/src/redfish/ris/validation.py +++ b/src/redfish/ris/validation.py @@ -19,19 +19,15 @@ # ---------Imports--------- -import re -import six import json import logging +import re import textwrap -import jsonpath_rw -try: - from collections import OrderedDict -except ImportError: - from collections.abc import OrderedDict +import six + from redfish.rest.containers import RisObject -from redfish.ris.utils import json_traversal + from .sharedtypes import JSONEncoder # ---------End of imports--------- @@ -116,10 +112,7 @@ def updatevalidationdata(self): self.defines.defs.regfilecollectiontype, ) ) - and any( - x.lower() in instance.path.lower() - for x in (self._schemaid, self._regid) - ) + and any(x.lower() in instance.path.lower() for x in (self._schemaid, self._regid)) and instance and instance.path not in self._classpaths ): @@ -205,7 +198,7 @@ def find_property(self, propname, cls=None, latestschema=False): dataloc = cls.get("Members", None) if not dataloc else dataloc keyword = "Schema" if dataloc and isinstance(dataloc, list): - splitname = propname.split(".")[0].strip("#") + _ = propname.split(".")[0].strip("#") propname = propname.split(".")[0].strip("#") if latestschema else propname for entry in dataloc: if entry: @@ -225,9 +218,7 @@ def find_property(self, propname, cls=None, latestschema=False): reglink = reglink[len(reglink) - 2] if reglink.lower().startswith(propname.lower()): self.monolith.load(path=entry["@odata.id"], crawl=False) - result.append( - self.monolith.paths[entry["@odata.id"]].dict - ) + result.append(self.monolith.paths[entry["@odata.id"]].dict) if result: result = max( @@ -314,11 +305,7 @@ def validatedict( ) ) orireg = reg.copy() - ttdict = { - key: val - for key, val in list(tdict.items()) - if not isinstance(val, (dict, list)) - } + ttdict = {key: val for key, val in list(tdict.items()) if not isinstance(val, (dict, list))} results = reg.validate_attribute_values(ttdict) self._errors.extend(results) @@ -357,14 +344,10 @@ def validatedict( del tdict[ki] else: - self._errors.append( - RegistryValidationError("Unable to locate registry model") - ) + self._errors.append(RegistryValidationError("Unable to locate registry model")) return self._errors, self._warnings - def checkreadunique( - self, tdict, tkey, reg=None, warnings=None, unique=None, searchtype=None - ): + def checkreadunique(self, tdict, tkey, reg=None, warnings=None, unique=None, searchtype=None): """Check for and remove the readonly and unique attributes if required. :param tdict: the dictionary to test against. @@ -389,20 +372,12 @@ def checkreadunique( if not unique and reg.get("IsSystemUniqueProperty", None): if tdict[tkey] and not isinstance(tdict[tkey], bool): - self._warnings.append( - "Property '%s' is unique and override not authorized. Skipping...\n" - % str(tkey) - ) + self._warnings.append("Property '%s' is unique and override not authorized. Skipping...\n" % str(tkey)) del tdict[tkey] return True - if not reg.get("ReadOnly") or ( - reg.get(tkey, None) and not reg[tkey].get("readonly") - ): + if not reg.get("ReadOnly") or (reg.get(tkey, None) and not reg[tkey].get("readonly")): if unique and reg.get("IsSystemUniqueProperty", None): - self._warnings.append( - "Property '%s' is unique, but override authorized...Patching..\n" - % str(tkey) - ) + self._warnings.append("Property '%s' is unique, but override authorized...Patching..\n" % str(tkey)) return False # if not searchtype or (reg.get("ReadOnly") or (reg.get(tkey) # and reg[tkey].get("readonly"))): @@ -411,9 +386,7 @@ def checkreadunique( # return True elif reg.get("ReadOnly") or (reg.get(tkey) and reg[tkey].get("readonly")): if tdict[tkey]: - self._warnings.append( - "Property '%s' is read-only. Skipping...\n" % str(tkey) - ) + self._warnings.append("Property '%s' is read-only. Skipping...\n" % str(tkey)) del tdict[tkey] return True else: @@ -448,11 +421,7 @@ def get_registry_model( """ regdict = None monolith = self.monolith - currtype = ( - currtype.split("#")[-1].split(".")[0] + "." - if currtype and latestschema - else currtype - ) + currtype = currtype.split("#")[-1].split(".")[0] + "." if currtype and latestschema else currtype if ( not currtype or not self.find_prop( @@ -482,8 +451,7 @@ def get_registry_model( ) or ( searchtype != "object" - and currtype.split("#")[-1].split(".")[0] - == instance.dict.get("RegistryPrefix", "") + and currtype.split("#")[-1].split(".")[0] == instance.dict.get("RegistryPrefix", "") ) ): regdict = instance.resp.dict @@ -504,10 +472,7 @@ def get_registry_model( if "RegistryEntries" in jsonreg: regitem = jsonreg["RegistryEntries"] if "Attributes" in regitem: - newitem = { - item[Typepathforval.typepath.defs.attributenametype]: item - for item in regitem["Attributes"] - } + newitem = {item[Typepathforval.typepath.defs.attributenametype]: item for item in regitem["Attributes"]} regitem["Attributes"] = newitem if not Typepathforval.typepath.flagiften: del regitem["Attributes"] @@ -536,14 +501,10 @@ def nestedreg(self, reg=None, args=None): """ for arg in args: try: - arg = next( - (key for key in list(reg.keys()) if key.lower() == arg.lower()), None - ) + arg = next((key for key in list(reg.keys()) if key.lower() == arg.lower()), None) if not arg: return None - if ("properties" in reg[arg].keys()) and ( - "patternProperties" in reg[arg].keys() - ): + if ("properties" in reg[arg].keys()) and ("patternProperties" in reg[arg].keys()): reg[arg]["properties"].update(reg[arg]["patternProperties"]) reg = reg[arg]["properties"] elif "oneOf" in reg[arg]: @@ -557,9 +518,7 @@ def nestedreg(self, reg=None, args=None): and "properties" in reg[arg]["items"] ): reg = reg[arg]["items"]["properties"] - elif (not "properties" in reg[arg].keys()) or ( - "patternProperties" in reg[arg].keys() - ): + elif ("properties" not in reg[arg].keys()) or ("patternProperties" in reg[arg].keys()): reg = reg[arg] else: reg = reg[arg]["properties"] @@ -587,12 +546,10 @@ def validate_attribute_values(self, tdict): result = list() for tkey in tdict: - if not tkey in self: + if tkey not in self: # Added for Gen 9 Bios properties not in registry continue - elif self[tkey] and ( - checkattr(self[tkey], "type") or checkattr(self[tkey], "Type") - ): + elif self[tkey] and (checkattr(self[tkey], "type") or checkattr(self[tkey], "Type")): keyval = list() keyval.append(tdict[tkey]) temp = self.validate_attribute(self[tkey], keyval, tkey) @@ -652,9 +609,7 @@ def get_validator(self, attrname, newargs=None, oneof=None): validator = PasswordValidator.parse(self[attrname]) elif "oneOf" in list(self[attrname].keys()): for item in self[attrname]["oneOf"]: - validator = self.get_validator( - attrname, newargs, HpPropertiesRegistry({attrname: item}) - ) + validator = self.get_validator(attrname, newargs, HpPropertiesRegistry({attrname: item})) if validator: break return validator @@ -820,8 +775,7 @@ def is_array(self, attrentry, arrval, name): else: result.append( RegistryValidationError( - "'%s' is not a valid setting " - "for '%s', expecting an array" % (arrval[0], name), + "'%s' is not a valid setting " "for '%s', expecting an array" % (arrval[0], name), regentry=self, ) ) @@ -882,10 +836,7 @@ def validate(self, keyval, name): possibleval and ( isinstance(possibleval, type(newval)) - or ( - isinstance(possibleval, six.string_types) - and isinstance(newval, six.string_types) - ) + or (isinstance(possibleval, six.string_types) and isinstance(newval, six.string_types)) ) and possibleval.lower() == str(newval).lower() ): @@ -898,9 +849,7 @@ def validate(self, keyval, name): return result result.append( - RegistryValidationError( - "'%s' is not a valid setting " "for '%s'" % (newval, name), regentry=self - ) + RegistryValidationError("'%s' is not a valid setting " "for '%s'" % (newval, name), regentry=self) ) return result @@ -970,9 +919,7 @@ def validate(self, newval, name): return result result.append( - RegistryValidationError( - "'%s' is not a valid setting for '%s'" % (newval[0], name), regentry=self - ) + RegistryValidationError("'%s' is not a valid setting for '%s'" % (newval[0], name), regentry=self) ) return result @@ -1040,8 +987,7 @@ def validate(self, newvallist, _): if len(newval) < int(self["MinLength"]): result.append( RegistryValidationError( - "'%s' must be at least '%s' characters long" - % (self[namestr], int(self["MinLength"])), + "'%s' must be at least '%s' characters long" % (self[namestr], int(self["MinLength"])), regentry=self, ) ) @@ -1050,8 +996,7 @@ def validate(self, newvallist, _): if len(newval) > int(self["MaxLength"]): result.append( RegistryValidationError( - "'%s' must be less than '%s' characters long" - % (self[namestr], int(self["MaxLength"])), + "'%s' must be less than '%s' characters long" % (self[namestr], int(self["MaxLength"])), regentry=self, ) ) @@ -1062,8 +1007,7 @@ def validate(self, newvallist, _): if newval and not pat.match(newval): result.append( RegistryValidationError( - "'%s' must match the regular expression " - "'%s'" % (self[namestr], self["ValueExpression"]), + "'%s' must match the regular expression " "'%s'" % (self[namestr], self["ValueExpression"]), regentry=self, ) ) @@ -1118,10 +1062,7 @@ def is_type(attrentry): if value.lower() == "integer" or value.lower() == "number": return True else: - if ( - attrentry["type"].lower() == "integer" - or attrentry["type"].lower().lower() == "number" - ): + if attrentry["type"].lower() == "integer" or attrentry["type"].lower().lower() == "number": return True elif "Type" in attrentry: if attrentry["Type"].lower() == "integer": @@ -1141,27 +1082,18 @@ def validate(self, newvallist, _): intval = int(newvallist[0]) newvallist[0] = intval except: - result.append( - RegistryValidationError( - "'%(Name)s' must " "be an integer value'" % (self), regentry=self - ) - ) + result.append(RegistryValidationError("'%(Name)s' must " "be an integer value'" % (self), regentry=self)) return result if newvallist[0] and not str(intval).isdigit(): - result.append( - RegistryValidationError( - "'%(Name)s' must " "be an integer value'" % (self), regentry=self - ) - ) + result.append(RegistryValidationError("'%(Name)s' must " "be an integer value'" % (self), regentry=self)) return result if "LowerBound" in self: if intval < int(self["LowerBound"]): result.append( RegistryValidationError( - "'%s' must be greater" - " than or equal to '%s'" % (self.Name, int(self["LowerBound"])), + "'%s' must be greater" " than or equal to '%s'" % (self.Name, int(self["LowerBound"])), regentry=self, ) ) @@ -1170,8 +1102,7 @@ def validate(self, newvallist, _): if intval > int(self["UpperBound"]): result.append( RegistryValidationError( - "'%s' must be less " - "than or equal to '%s'" % (self.Name, int(self["LowerBound"])), + "'%s' must be less " "than or equal to '%s'" % (self.Name, int(self["LowerBound"])), regentry=self, ) ) @@ -1323,8 +1254,7 @@ def validate(self, newvallist, _): if len(newval) < int(self["MinLength"]): result.append( RegistryValidationError( - "'%s' must be at least" - " '%s' characters long" % (self.Name, int(self["MinLength"])), + "'%s' must be at least" " '%s' characters long" % (self.Name, int(self["MinLength"])), regentry=self, ) ) @@ -1333,8 +1263,7 @@ def validate(self, newvallist, _): if len(newval) > int(self["MaxLength"]): result.append( RegistryValidationError( - "'%s' must be less " - "than '%s' characters long" % (self.Name, int(self["MaxLength"])), + "'%s' must be less " "than '%s' characters long" % (self.Name, int(self["MaxLength"])), regentry=self, ) ) @@ -1345,9 +1274,7 @@ def validate(self, newvallist, _): if newval and not pat.match(newval): result.append( RegistryValidationError( - "'%(Name)s' must " - "match the regular expression '%(Value" - "Expression)s'" % (self), + "'%(Name)s' must " "match the regular expression '%(Value" "Expression)s'" % (self), regentry=self, ) )