### # Copyright 2020 Hewlett Packard Enterprise, Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ### # -*- coding: utf-8 -*- """Base implementation for interaction with blob store interface""" # ---------Imports--------- import os import sys import platform import sysconfig import logging import random import struct import string import time import ctypes from ctypes import POINTER, c_char_p, c_ubyte, c_uint, c_ushort, c_void_p, create_string_buffer from redfish.hpilo.rishpilo import BlobReturnCodes as hpiloreturncodes from redfish.hpilo.rishpilo import ( HpIlo, HpIloChifPacketExchangeError, HpIloInitialError, ) if os.name == "nt": from ctypes import windll else: from _ctypes import dlclose # Flags from (Linux/Unix) RTLD_LAZY = 0x00001 RTLD_GLOBAL = 0x00100 # ---------End of imports--------- # ---------Debug logger--------- LOGGER = logging.getLogger(__name__) # ---------End of debug logger--------- # -----------------------Error Returns---------------------- class UnexpectedResponseError(Exception): """Raise when we get data that we don't expect from iLO""" pass class HpIloError(Exception): """Raised when iLO returns non-zero error code""" pass class Blob2CreateError(Exception): """Raised when create operation fails""" pass class Blob2InfoError(Exception): """Raised when create operation fails""" pass class Blob2ReadError(Exception): """Raised when read operation fails""" pass class Blob2WriteError(Exception): """Raised when write operation fails""" pass class Blob2DeleteError(Exception): """Raised when delete operation fails""" pass class Blob2OverrideError(Exception): """Raised when delete operation fails because of it been overwritten""" pass class BlobRetriesExhaustedError(Exception): """Raised when max retries have been attempted for same operation""" pass class Blob2FinalizeError(Exception): """Raised when finalize operation fails""" pass class Blob2ListError(Exception): """Raised when list operation fails""" pass class Blob2SecurityError(Exception): """Raised when there is an issue with security""" pass class BlobNotFoundError(Exception): """Raised when blob not found in key/namespace""" pass class ChifDllMissingError(Exception): """Raised when unable to obtain ilorest_chif dll handle""" pass class EncryptionEnabledError(Exception): """Raised when high security encryption is enabled""" pass # ---------------------------------------------------------- # -------------------Helper functions------------------------- class BlobReturnCodes(object): """Blob store return codes. SUCCESS success BADPARAMETER bad parameter supplied NOTFOUND blob name not found NOTMODIFIED call did not perform the operation """ SUCCESS = 0 BADPARAMETER = 2 NOTFOUND = 12 NOTMODIFIED = 20 class BlobStore2(object): """Blob store 2 class""" def __init__(self, log_dir=None): lib = self.gethprestchifhandle() self.log_dir = log_dir self.channel = HpIlo(dll=lib, log_dir=log_dir) self.max_retries = 44 self.max_read_retries = 3 self.delay = 0.25 LOGGER.info("BlobStore initialized with log directory: %s", log_dir) def __del__(self): """Blob store 2 close channel function""" if hasattr(self, "channel"): self.channel.close() def create(self, key, namespace): """ Create the blob. :param key: The blob key to create. :type key: str :param namespace: The blob namespace to create the key in. :type namespace: str :return: The response from the blob creation request. :rtype: bytearray :raises HpIloError: If the operation fails. """ LOGGER.info("Attempting to create blob: Key=%s, Namespace=%s", key, namespace) try: lib = self.gethprestchifhandle() lib.create_not_blobentry.argtypes = [c_char_p, c_char_p] lib.create_not_blobentry.restype = POINTER(c_ubyte) name = create_string_buffer(key.encode("utf-8")) namespace = create_string_buffer(namespace.encode("utf-8")) LOGGER.debug("Calling create_not_blobentry()") ptr = lib.create_not_blobentry(name, namespace) LOGGER.debug("Preparing request data") data = ptr[: lib.size_of_createRequest()] data = bytearray(data) LOGGER.info("Sending blob creation request") resp = self._send_receive_raw(data) errorcode = struct.unpack("= 12: errorcode = struct.unpack(" 0: LOGGER.info("High security mode detected. Authenticating credentials.") LOGGER.debug(f"Security requirements: Username={username}, High Security Mode=True") dll.initiate_credentials.argtypes = [c_char_p, c_char_p] dll.initiate_credentials.restype = POINTER(c_ubyte) usernew = create_string_buffer(username.encode("utf-8")) passnew = create_string_buffer(password.encode("utf-8")) LOGGER.debug("Initiating credential verification.") dll.initiate_credentials(usernew, passnew) credreturn = dll.ChifVerifyCredentials() if credreturn == BlobReturnCodes.SUCCESS: LOGGER.info("Credentials verified successfully.") elif credreturn == hpiloreturncodes.CHIFERR_AccessDenied: LOGGER.error("Access Denied: Invalid credentials.") raise Blob2SecurityError() else: LOGGER.error("Error %s occurred while trying to open a channel to iLO.", credreturn) raise HpIloInitialError(f"Error {credreturn} occurred while trying to open a channel to iLO.") else: LOGGER.info("Security not required. Disabling security.") dll.ChifDisableSecurity() else: LOGGER.debug("No username provided. Checking security requirement.") if dll.ChifIsSecurityRequired() > 0: LOGGER.warning("High security mode detected but no credentials provided.") return False else: LOGGER.info("Security not required. Disabling security.") dll.ChifDisableSecurity() return True except Exception as e: LOGGER.exception("Exception occurred during Chif initialization: %s", str(e)) raise finally: LOGGER.debug("Unloading Chif handle.") BlobStore2.unloadchifhandle(dll) @staticmethod def checkincurrdirectory(libname): """Check if the library is present in current directory. :param libname: The name of the library to search for. :type libname: str.""" libpath = libname if os.path.isfile(os.path.join(os.path.split(sys.executable)[0], libpath)): libpath = os.path.join(os.path.split(sys.executable)[0], libpath) elif os.path.isfile(os.path.join(os.getcwd(), libpath)): 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 = libpath[0] if libpath else libname return libpath @staticmethod def unloadchifhandle(lib): """ Release a handle on the Chif iLOrest library. :param lib: The library handle provided by loading the Chif library. :type lib: library handle """ if lib is None: LOGGER.warning("unloadchifhandle called with None library handle. Nothing to release.") return try: libhandle = lib._handle LOGGER.info("Releasing Chif library handle: %s", libhandle) if os.name == "nt": LOGGER.debug("Using Windows FreeLibrary to release handle.") windll.kernel32.FreeLibrary(None, handle=libhandle) else: LOGGER.debug("Using dlclose to release handle on Linux/Unix.") dlclose(libhandle) LOGGER.info("Successfully released Chif library handle.") except Exception as e: LOGGER.exception("Error while unloading Chif library handle: %s", str(e))