460 lines
19 KiB
Python
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
|