Files

460 lines
19 KiB
Python

###
# 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 -*-
"""Compatibility functionality in between iLO versions and generic Redfish/LegacyRest servers.
Used to provide convenient string variables that are usable on any iLO irrespective of version or
API type."""
# ---------Imports---------
import logging
from redfish import LegacyRestClient, RedfishClient
from redfish.rest.v1 import ServerDownOrUnreachableError
from redfish.ris.rmc_helper import (
NothingSelectedError,
UnableToObtainIloVersionError,
UserNotAdminError,
)
# ---------End of imports---------
LOGGER = logging.getLogger(__name__)
# TODO: This will be a part of the compatability class
class Typesandpathdefines(object):
"""The global types and path definitions class. Holds information on a system and automatically
creates the correct type strings along with some generic paths. Paths are meant to be used with
iLO systems. Paths may be different on generic Redfish systems. Self variables are created when
the `getgen` function is called.
Useful self variables that are created include:
* **url**: The url of the system that the defines were created for.
* **defs**: The string defines for the system that was passed to `getgen`. Includes key
property keys, paths, types, and flags to check what the system type is.
* **ilogen**: The iLO generation of the system that the defines were created for. For non-iLO
Redfish systems this is set to **5**.
* **iloversion**: The iLO version of the system that the defines were created for.
* **flagiften**: Flag is set to true if the system is Gen 10 or a non-iLO Redfish system.
"""
def __init__(self):
self.url = None
self.defs = None
self.ilogen = None
self.iloversion = None
self.flagiften = False
self.adminpriv = True
def iLO7VersionCheck(self, rootresp):
if "Oem" in rootresp:
if "Hpe" in rootresp["Oem"]:
gen = next(iter(rootresp.get("Oem", {}).get("Hpe", {}).get("Manager", {}))).get("ManagerType", None)
elif "Hp" in rootresp["Oem"]:
gen = next(iter(rootresp.get("Oem", {}).get("Hp", {}).get("Manager", {}))).get("ManagerType", None)
else:
raise Exception("Malformed Json")
if gen:
gen = gen.split(" ")[1]
else:
gen = rootresp.get("Oem", {}).get("Hpe", {}).get("Moniker", {}).get("PRODGEN").split(" ")[1]
if int(gen) == 7:
return True
return False
def getgen(
self,
gen=None,
url=None,
username=None,
password=None,
sessionid=None,
logger=None,
proxy=None,
ca_cert_data={},
isredfish=True,
login_otp=None,
log_dir=None,
session_location=None,
):
"""Function designed to verify the servers platform. Will generate the `Typeandpathdefines`
variables based on the system type that is detected.
:param url: The URL to perform the request on.
:type url: str
:param username: The username to login with.
:type username: str
:param password: The password to login with.
:type password: str
:param proxy: The proxy to connect to the system with.
:type proxy: str
:param ca_certs: Dictionary including the TLS certificate information of user based
authentication
:type ca_certs: dict
:param isredfish: The flag to force redfish conformance on iLO 4 systems. You will still
need to call `updatedefinesflag` to update the defines to Redfish.
:type isredfish: bool
:param logger: The logger handler to log data too uses the default if none is provided.
:type logger: str
"""
if self.adminpriv is False and url.startswith("blob"):
raise UserNotAdminError("")
self.url = url
self.is_redfish = isredfish
self.gencompany = self.rootresp = False
self.ilogen = 5 # If no iLO or Anonymous data , default to iLO 5 types
logger = logger if not logger else LOGGER
client = None
self.noschemas = False
self.schemapath = self.regpath = ""
if not gen:
try_count = 0
try:
client = RedfishClient(
base_url=self.url,
username=username,
password=password,
sessionid=sessionid,
proxy=proxy,
ca_cert_data=ca_cert_data,
login_otp=login_otp,
log_dir=log_dir,
session_location=session_location,
)
client._get_root()
except ServerDownOrUnreachableError as excp:
if self.is_redfish:
raise excp
try_count += 1
if not self.is_redfish:
try:
restclient = LegacyRestClient(
base_url=self.url,
username=username,
password=password,
sessionid=sessionid,
proxy=proxy,
ca_cert_data=ca_cert_data,
login_otp=login_otp,
session_location=session_location,
)
restclient._get_root()
# Check that the response is actually legacy rest and not a redirect
_ = restclient.root.obj.Type
self.is_redfish = False
client = restclient
except Exception as excp:
try_count += 1
if not client:
logger.info("Gen get rest error:" + str(excp) + "\n")
raise excp
else:
self.is_redfish = True
if try_count > 1:
raise ServerDownOrUnreachableError("Server not reachable or does not support " "HPRest or Redfish \n")
rootresp = client.root.obj
self.rootresp = rootresp
client.logout()
iLO7 = False
iLO7 = self.iLO7VersionCheck(rootresp)
if not iLO7:
self.gencompany = next(iter(self.rootresp.get("Oem", {}).keys()), None) in (
"Hpe",
"Hp",
)
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 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(".")))
else:
self.ilogen = next(iter(rootresp.get("Oem", {}).get("Hpe", {}).get("Manager", {}))).get("ManagerType")
self.ilover = next(iter(rootresp.get("Oem", {}).get("Hpe", {}).get("Manager", {}))).get(
"ManagerFirmwareVersion", None
)
if self.ilover is None:
self.ilover = "1.01.01"
self.iloversion = float(self.ilogen.split(" ")[-1] + "." + "".join(self.ilover.split(".")))
else:
self.ilogen = int(gen)
try:
if not isinstance(self.ilogen, int):
self.ilogen = int(self.ilogen.split(" ")[-1])
self.flagiften = True if int(self.ilogen) >= 5 else False
except:
raise UnableToObtainIloVersionError("Unable to find the iLO generation.")
self.noschemas = (
True
if self.rootresp and "JsonSchemas" in self.rootresp and not self.rootresp.get("JsonSchemas", None)
else False
)
if self.noschemas:
self.ilogen = self.ilover = self.iloversion = None
if self.rootresp and not self.noschemas:
self.defineregschemapath(self.rootresp)
if self.flagiften:
self.defs = Definevalstenplus()
else:
self.defs = DefinevalsNine()
def defineregschemapath(self, rootobj):
"""Defines the schema and registry paths using data in root path.
:param rootobj: The root path data.
:type rootobj: dict.
"""
if self.iLO7VersionCheck(rootobj):
self.gencompany = "Hpe"
else:
self.gencompany = next(iter(rootobj.get("Oem", {}).keys()), None) in ("Hpe", "Hp")
self.schemapath = (
rootobj["JsonSchemas"]["@odata.id"]
if rootobj.get("JsonSchemas", None)
else rootobj["links"]["Schemas"]["href"]
)
self.schemapath = (
self.schemapath.rstrip("/") + "/?$expand=."
if self.is_redfish and self.flagiften and self.gencompany
else self.schemapath
)
self.regpath = (
rootobj["Registries"]["@odata.id"]
if rootobj.get("Registries", None)
else rootobj["links"]["Registries"]["href"]
)
self.regpath = (
self.regpath.rstrip("/") + "/?$expand=."
if self.is_redfish and self.flagiften and self.gencompany
else self.regpath
)
# TODO: Move these to a compatability class
def updatedefinesflag(self, redfishflag=None):
"""Updates the redfish and rest flag depending on system and redfishflag input. On an iLO 5
system or another Redfish system, this will do nothing. On an iLO 4 system with both Redfish
and LegacyRest this will update the defines to redfish if the *redfishflag* is set to True
and stay with the LegacyRest defines otherwise.
:param redfishflag: User input for redfish
:type redfishflag: bool
:returns: True if the system should use Redfish, False for legacy Rest.
:rtype: bool
"""
if self.defs:
is_redfish = redfishflag or self.defs.isgen10
self.defs.flagforrest = not is_redfish
if is_redfish:
self.defs.redfishchange()
return is_redfish
else:
return redfishflag
def modifyselectorforgen(self, sel):
"""Changes the select to match the Generation's HP string based to the correct type for
specific iLO versions.
:param sel: query to be changed to match Generation's HP string
:type sel: str
:returns: A modified selector matching the Generation's HP string.
:rtype: string
"""
if not sel:
raise NothingSelectedError()
sel = sel.lower()
returnval = sel
if sel.startswith(("hpeeskm", "#hpeeskm", "hpeskm", "#hpeskm")):
returnval = self.defs.hpeskmtype
elif "bios." in sel[:9].lower():
returnval = self.defs.biostype
elif sel.startswith(("hpe", "#hpe")) and self.defs and self.defs.isgen9:
returnval = sel[:4].replace("hpe", "hp") + sel[4:]
elif not sel.startswith(("hpe", "#hpe")) and self.defs and self.defs.isgen10:
returnval = sel[:3].replace("hp", "hpe") + sel[3:]
return returnval
class Definevals(object):
"""Base class for setting platform dependent variables."""
def __init__(self):
pass
class Definevalstenplus(Definevals):
"""Platform dependent variables for iLO 5+ (Gen 10)."""
# pylint: disable=too-many-instance-attributes
# As a defines class this will need all the attributes
def __init__(self):
self.oemhp = "Hpe"
self.oempath = "/Oem/Hpe"
self.startpath = "/redfish/v1/"
self.systempath = "/redfish/v1/Systems/1/"
self.managerpath = "/redfish/v1/Managers/1/"
self.biospath = "/redfish/v1/systems/1/bios/"
self.addlicensepath = "/redfish/v1/Managers/1/LicenseService/"
self.accountspath = "/redfish/v1/AccountService/Accounts/"
self.federationpath = "/redfish/v1/Managers/1/FederationGroups/"
self.resourcedirpath = "/redfish/v1/ResourceDirectory/"
self.biostype = "Bios."
self.hpeskmtype = "HpeESKM."
self.hpcommontype = "HpeCommon"
self.hpilossotype = "HpeiLOSSO."
self.hpsecureboot = "SecureBoot."
self.logservicetype = "#LogService."
self.iscsisource = "iSCSISources"
self.iscsiattemptinstance = "iSCSIAttemptInstance"
self.iscsiattemptname = "iSCSIAttemptName"
self.hphttpscerttype = "HpeHttpsCert."
self.snmpservice = "HpeiLOSnmpService."
self.attributenametype = "AttributeName"
self.hpilodatetimetype = "HpeiLODateTime."
self.attributeregtype = "#AttributeRegistry."
self.hpilofirmwareupdatetype = "UpdateService."
self.resourcedirectorytype = "HpeiLOResourceDirectory."
self.hpilofederationgrouptype = "HpeiLOFederationGroup."
self.managernetworkservicetype = "ManagerNetworkProtocol."
self.schemafilecollectiontype = "#JsonSchemaFileCollection."
self.regfilecollectiontype = "#MessageRegistryFileCollection."
self.hpilolicensecollectiontype = "HpeiLOLicenseCollection."
self.hpiloactivehealthsystemtype = "#HpeiLOActiveHealthSystem."
self.securityservice = "HpeSecurityService."
self.hpiscsisoftwareinitiatortype = "HpeiSCSISoftwareInitiator."
self.hpilofederationgrouptypecoll = "HpeiLOFederationGroupCollection."
self.bootoverridetargettype = "BootSourceOverrideTarget@Redfish.AllowableValues"
self.messageregistrytype = "#MessageRegistry."
self.typestring = "@odata.type"
self.hrefstring = "@odata.id"
self.collectionstring = "Members"
self.biossettingsstring = "@Redfish.Settings"
self.attname = "AttributeName"
self.iscsistring = "iSCSISources"
self.isgen9 = False
self.isgen10 = True
self.flagforrest = False
super(Definevalstenplus, self).__init__()
def redfishchange(self):
"""Empty function to update redfish variables (unneeded when the system is already redfish)."""
pass
class DefinevalsNine(Definevals):
"""Platform dependent variables for iLO 4 LegacyRest (Gen 9)."""
# pylint: disable=too-many-instance-attributes
# As a defines class this will need all the attributes
def __init__(self):
self.oemhp = "Hp"
self.oempath = "/Oem/Hp"
self.startpath = "/rest/v1"
self.systempath = "/rest/v1/Systems/1"
self.managerpath = "/rest/v1/Managers/1"
self.biospath = "/rest/v1/systems/1/bios"
self.addlicensepath = "/rest/v1/Managers/1/LicenseService"
self.accountspath = "/rest/v1/AccountService/Accounts"
self.federationpath = "/rest/v1/Managers/1/FederationGroups"
self.resourcedirpath = "/rest/v1/ResourceDirectory"
self.biostype = "HpBios."
self.hpeskmtype = "HpESKM."
self.hpcommontype = "HpCommon"
self.hpilossotype = "HpiLOSSO."
self.snmpservice = "SnmpService."
self.attributenametype = "Name"
self.logservicetype = "LogService."
self.iscsisource = "iSCSIBootSources"
self.iscsiattemptinstance = "iSCSIBootAttemptInstance"
self.iscsiattemptname = "iSCSIBootAttemptName"
self.hpsecureboot = "HpSecureBoot."
self.hphttpscerttype = "HpHttpsCert."
self.hpilodatetimetype = "HpiLODateTime."
self.hpilofirmwareupdatetype = "HpiLOFirmwareUpdate."
self.resourcedirectorytype = "HpiLOResourceDirectory."
self.hpilofederationgrouptype = "HpiLOFederationGroup."
self.attributeregtype = "HpBiosAttributeRegistrySchema."
self.schemafilecollectiontype = "#SchemaFileCollection."
self.regfilecollectiontype = "#SchemaFileCollection."
self.managernetworkservicetype = "ManagerNetworkService."
self.hpiloactivehealthsystemtype = "HpiLOActiveHealthSystem."
self.messageregistrytype = "MessageRegistry."
self.hpilolicensecollectiontype = None
self.hpilofederationgrouptypecoll = None
self.bootoverridetargettype = "BootSourceOverrideSupported"
self.hpiscsisoftwareinitiatortype = "HpiSCSISoftwareInitiator"
self.typestring = "Type"
self.hrefstring = "href"
self.collectionstring = "Items"
self.biossettingsstring = "SettingsResult"
self.attname = "Name"
self.iscsistring = "iSCSIBootSources"
self.isgen9 = True
self.isgen10 = False
self.flagforrest = True
super(DefinevalsNine, self).__init__()
def redfishchange(self):
"""Function to update redfish variables from LegacyRest to iLO 4 Redfish (Gen 9)."""
self.startpath = "/redfish/v1/"
self.systempath = "/redfish/v1/Systems/1/"
self.managerpath = "/redfish/v1/Managers/1/"
self.biospath = "/redfish/v1/systems/1/bios/"
self.addlicensepath = "/redfish/v1/Managers/1/LicenseService/"
self.resourcedirpath = "/redfish/v1/ResourceDirectory/"
self.typestring = "@odata.type"
self.hrefstring = "@odata.id"
self.collectionstring = "Members"
self.logservicetype = "#LogService."
self.hpiloactivehealthsystemtype = "#HpiLOActiveHealthSystem."
self.hpilolicensecollectiontype = "HpiLOLicenseCollection."
self.hpilofederationgrouptypecoll = "HpiLOFederationGroupCollection."
self.managernetworkservicetype = "ManagerNetworkProtocol."
self.flagforrest = False