diff --git a/examples/simple-proliant.py b/examples/simple-proliant.py index 806b4d9..cf9fcfb 100644 --- a/examples/simple-proliant.py +++ b/examples/simple-proliant.py @@ -6,13 +6,13 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() from builtins import str import os import sys import json import redfish +standard_library.install_aliases() # Get $HOME environment. @@ -52,46 +52,57 @@ print ("Redfish API version : %s \n" % remote_mgmt.get_api_version()) # Uncomment following line to reset the blade !!! # remote_mgmt.Systems.systems_dict["1"].reset_system() -# TODO : create an attribute to link the managed system directly -# and avoid systems_dict["1"] -# --> will be something like : -# remote_mgmt.Systems.systems_dict["1"] = remote_mgmt.Systems.managed_system +print("Bios version : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_bios_version())) +print("Serial Number : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_serial_number())) +print("Power State : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_power())) +print("Parameter 'SystemType' : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_parameter("SystemType"))) -print("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_bios_version())) -print("Serial Number : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_serial_number())) -print("Power State : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_power())) -print("Parameter 'SystemType' : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_parameter("SystemType"))) +print("Get bios parameters : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].bios.get_parameters())) +print("Get boot parameters : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].bios.boot.get_parameters())) -print("Get bios parameters : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.get_parameters())) -print("Get boot parameters : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.boot.get_parameters())) +# print("Get bios parameter 'AdminPhone' : {}\n".format( +# remote_mgmt.Systems.systems_dict["1"].bios.get_parameter("AdminPhone"))) +# print("Set bios parameter 'AdminPhone' to '' : {}\n".format( +# remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("AdminPhone",""))) -#print("Get bios parameter 'AdminPhone' : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.get_parameter("AdminPhone"))) -#print("Set bios parameter 'AdminPhone' to '' : {}\n".format(remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("AdminPhone",""))) +# Boot server with script +# remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("Dhcpv4","Enabled") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter( + "PreBootNetwork", "Auto") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter( + "UefiShellStartup", "Enabled") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter( + "UefiShellStartupLocation", "NetworkLocation") +remote_mgmt.Systems.systems_dict["1"].bios.set_parameter( + "UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") -#Boot server with script -#remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("Dhcpv4","Enabled") - -remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("PreBootNetwork", "Auto") -remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("UefiShellStartup", "Enabled") -remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("UefiShellStartupLocation", "NetworkLocation") -remote_mgmt.Systems.systems_dict["1"].bios.set_parameter("UefiShellStartupUrl", "http://10.3.222.88/deploy/startup.nsh") - -#remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') -# remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') -#remote_mgmt.Systems.systems_dict["1"].set_parameter_json('{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') +# remote_mgmt.Systems.systems_dict["1"].set_parameter_json( +# '{"Boot": {"BootSourceOverrideTarget": "UefiShell"}}') +# remote_mgmt.Systems.systems_dict["1"].set_parameter_json( +# '{"Boot": {"BootSourceOverrideEnabled" : "Continuous"}}') +# remote_mgmt.Systems.systems_dict["1"].set_parameter_json( +# '{"Boot": {"BootSourceOverrideEnabled" : "Once"}}') mySystem = remote_mgmt.Systems.systems_dict["1"] -mySystem.set_boot_source_override("None","Disabled") -#Uncomment the next line to reset the server -#mySystem.reset_system() +mySystem.set_boot_source_override("None", "Disabled") +# Uncomment the next line to reset the server +# mySystem.reset_system() -print("Get manager firmware version : {}\n".format(remote_mgmt.Managers.managers_dict["1"].get_firmware_version())) -print("Get system Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_bios_version())) +print("Get manager firmware version : {}\n".format( + remote_mgmt.Managers.managers_dict["1"].get_firmware_version())) +print("Get system Bios version : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_bios_version())) -#Reset of the system is required to apply the changes -#remote_mgmt.Systems.systems_dict["1"].reset_system() +# Reset of the system is required to apply the changes +# remote_mgmt.Systems.systems_dict["1"].reset_system() remote_mgmt.logout() diff --git a/redfish-client/redfish-client b/redfish-client/redfish-client index cd88f02..951a849 100755 --- a/redfish-client/redfish-client +++ b/redfish-client/redfish-client @@ -38,7 +38,6 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() from builtins import str from builtins import object @@ -52,6 +51,7 @@ import configparser import jinja2 import requests.packages.urllib3 import redfish +standard_library.install_aliases() class InventoryFile(object): diff --git a/redfish/__init__.py b/redfish/__init__.py index 2b57f55..724f97e 100644 --- a/redfish/__init__.py +++ b/redfish/__init__.py @@ -17,10 +17,10 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() import pbr.version from redfish.main import * +standard_library.install_aliases() try: __version__ = pbr.version.VersionInfo('redfish').release_string() diff --git a/redfish/config.py b/redfish/config.py index 33391e4..ccd9dc7 100644 --- a/redfish/config.py +++ b/redfish/config.py @@ -8,7 +8,6 @@ from future import standard_library import logging import sys import os -import getpass from logging.handlers import RotatingFileHandler standard_library.install_aliases() @@ -30,8 +29,10 @@ if not os.path.exists(REDFISH_HOME): os.mkdir(REDFISH_HOME) except IOError: print('ERROR: can\'t create {}.\n'.format(REDFISH_HOME)) - print(' Try to create directory {}'.format(os.path.dirname(REDFISH_LOGFILE))) - print(' using: mkdir -p {}'.format(os.path.dirname(REDFISH_LOGFILE))) + print(' Try to create directory {}'.format( + os.path.dirname(REDFISH_LOGFILE))) + print(' using: mkdir -p {}'.format( + os.path.dirname(REDFISH_LOGFILE))) sys.exit(1) REDFISH_LOGFILE = os.path.join(REDFISH_HOME, "python-redfish.log") @@ -59,15 +60,18 @@ def initialize_logger(REDFISH_LOGFILE, logger = logging.getLogger(logger_name) logger.setLevel(logging.DEBUG) formatter = logging.Formatter( - '%(asctime)s :: %(levelname)s :: %(message)s' - ) + '%(asctime)s :: %(levelname)s :: %(message)s') try: - file_handler = RotatingFileHandler(os.path.expandvars(REDFISH_LOGFILE), 'a', 1000000, 1) + file_handler = RotatingFileHandler( + os.path.expandvars(REDFISH_LOGFILE), 'a', 1000000, 1) except IOError: - print('ERROR: {} does not exist or is not writeable.\n'.format(REDFISH_LOGFILE)) - print(' Try to create directory {}'.format(os.path.dirname(REDFISH_LOGFILE))) - print(' using: mkdir -p {}'.format(os.path.dirname(REDFISH_LOGFILE))) + print('ERROR: {} does not exist or is not writeable.\n'.format( + REDFISH_LOGFILE)) + print(' Try to create directory {}'.format(os.path.dirname( + REDFISH_LOGFILE))) + print(' using: mkdir -p {}'.format(os.path.dirname( + REDFISH_LOGFILE))) sys.exit(1) # First logger to file diff --git a/redfish/exception.py b/redfish/exception.py index 84b67cf..59e2016 100644 --- a/redfish/exception.py +++ b/redfish/exception.py @@ -5,9 +5,9 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() from builtins import str from . import config +standard_library.install_aliases() class RedfishException(Exception): @@ -28,7 +28,8 @@ class ConnectionFailureException(RedfishException): '3- Check if your device has a valid trusted certificat\n' + \ ' You can use openssl to validate it using the command :\n' + \ ' openssl s_client -showcerts -connect :443\n' + \ - '4- Use option "--insecure" to connect without checking certificate\n' + '4- Use option "--insecure" to connect without checking' + \ + ' certificate\n' class InvalidRedfishContentException(RedfishException): diff --git a/redfish/main.py b/redfish/main.py index 9bdf5f0..9b8267d 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -29,8 +29,9 @@ resources. A URI should be treated by the client as opaque, and thus should not be attempted to be understood or deconstructed by the client. Only specific top level URIs (any URI in this sample code) may be assumed, and even these may be -absent based upon the implementation (e.g. there might be no /redfish/v1/Systems -collection on something that doesn't have compute nodes.) +absent based upon the implementation +(e.g. there might be no /redfish/v1/Systems collection on something +that doesn't have compute nodes.) The other URIs must be discovered dynamically by following href links. This is because the API will eventually be implemented on a system that breaks any @@ -141,8 +142,7 @@ def connect( password, simulator=False, enforceSSL=True, - verify_cert=True - ): + verify_cert=True): return RedfishConnection( url, @@ -214,8 +214,10 @@ class RedfishConnection(object): mapping.redfish_version = self.get_api_version() mapping.redfish_root_name = self.Root.get_name() - # Instantiate a global mapping object to handle Redfish version variation - mapping.redfish_mapper = mapping.RedfishVersionMapping(self.get_api_version(), self.Root.get_name()) + # Instantiate a global mapping object to handle + # Redfish version variation + mapping.redfish_mapper = mapping.RedfishVersionMapping( + self.get_api_version(), self.Root.get_name()) # Now we need to login otherwise we are not allowed to extract data if self.__simulator is False: @@ -225,9 +227,8 @@ class RedfishConnection(object): config.logger.info("Login successful") except "Error getting token": config.logger.error("Login fail, fail to get auth token") - raise exception.AuthenticationFailureException("Fail to get an auth token.") - - + raise exception.AuthenticationFailureException( + "Fail to get an auth token.") # Structure change with mockup 1.0.0, there is no links # section anymore. @@ -237,30 +238,25 @@ class RedfishConnection(object): # Types self.SessionService = types.SessionService( - self.Root.get_link_url( - mapping.redfish_mapper.map_sessionservice()), - self.connection_parameters - ) + self.Root.get_link_url( + mapping.redfish_mapper.map_sessionservice()), + self.connection_parameters) - self.Managers = types.ManagersCollection(self.Root.get_link_url("Managers"), - self.connection_parameters - ) + self.Managers = types.ManagersCollection( + self.Root.get_link_url("Managers"), + self.connection_parameters) - self.Systems = types.SystemsCollection(self.Root.get_link_url("Systems"), - self.connection_parameters - ) + self.Systems = types.SystemsCollection( + self.Root.get_link_url("Systems"), + self.connection_parameters) - self.Chassis = types.ChassisCollection(self.Root.get_link_url("Chassis"), - self.connection_parameters - ) + self.Chassis = types.ChassisCollection( + self.Root.get_link_url("Chassis"), self.connection_parameters) # self.EventService # self.AccountService # self.Tasks - - - # ======================================================================== # systemCollectionLink = getattr(self.root.Links.Systems,"@odata.id") # self.systemCollection = self.apiUrl.redfish.v1.Systems.get() @@ -280,8 +276,7 @@ class RedfishConnection(object): def login(self): # Craft full url url = self.Root.get_link_url( - mapping.redfish_mapper.map_sessionservice() - ) + mapping.redfish_mapper.map_sessionservice()) # Handle login with redfish 1.00, url must be : # /rest/v1/SessionService/Sessions as specified by the specification @@ -293,14 +288,17 @@ class RedfishConnection(object): "Password": self.connection_parameters.password} config.logger.debug(requestBody) headers = self.connection_parameters.headers - # ======================================================================= - # Tortilla seems not able to provide the header of a post request answer. + # ==================================================================== + # Tortilla seems not able to provide the header of a post request + # answer. # However this is required by redfish standard to get X-Auth-Token. # So jump to "requests" library to get the required token. # TODO : Patch tortilla to handle this case. - # ======================================================================= - # sessionsUrl = tortilla.wrap("https://10.3.222.104/rest/v1/Sessions", debug=TORTILLADEBUG) - # sessions = sessionsUrl.post(verify=self.verify_cert, data=requestBody) + # ==================================================================== + # sessionsUrl = tortilla.wrap( + # "https://10.3.222.104/rest/v1/Sessions", debug=TORTILLADEBUG) + # sessions = sessionsUrl.post( + # verify=self.verify_cert, data=requestBody) auth = requests.post(url, data=json.dumps(requestBody), headers=headers, diff --git a/redfish/mapping.py b/redfish/mapping.py index db116ab..d0906dd 100644 --- a/redfish/mapping.py +++ b/redfish/mapping.py @@ -4,13 +4,14 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() from builtins import object +standard_library.install_aliases() redfish_mapper = None redfish_version = None redfish_root_name = None + class RedfishVersionMapping(object): '''Implements basic url path mapping beetween Redfish versions.''' @@ -24,7 +25,7 @@ class RedfishVersionMapping(object): return 'SessionService' def map_links(self, data_dict=None): - if data_dict == None: + if data_dict is None: if self.__version == '0.95': return 'links' else: @@ -39,7 +40,7 @@ class RedfishVersionMapping(object): return 'Links' def map_links_ref(self, data_dict=None): - if data_dict == None: + if data_dict is None: if self.__version == '0.95': return 'href' else: @@ -49,11 +50,11 @@ class RedfishVersionMapping(object): try: data_dict.href return 'href' - except AttributeError: - pass + except AttributeError: + pass return '@odata.id' - + def map_members(self): if self.__version == '0.95': return 'Member' - return 'Members' \ No newline at end of file + return 'Members' diff --git a/redfish/types.py b/redfish/types.py index 0ee1b35..3a5b4fc 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -28,7 +28,7 @@ class Base(object): def __init__(self, url, connection_parameters): '''Class constructor''' global TORTILLADEBUG - self.connection_parameters = connection_parameters # Uggly hack to check + self.connection_parameters = connection_parameters # Uggly hack self.url = url self.api_url = tortilla.wrap(url, debug=config.TORTILLADEBUG) @@ -69,7 +69,9 @@ class Base(object): if float(mapping.redfish_version) < 1.00: links = getattr(self.data, mapping.redfish_mapper.map_links()) if link_type in links: - return urljoin(self.url, links[link_type][mapping.redfish_mapper.map_links_ref()]) + return urljoin( + self.url, + links[link_type][mapping.redfish_mapper.map_links_ref()]) raise AttributeError else: links = getattr(self.data, link_type) @@ -147,17 +149,22 @@ class BaseCollection(Base): self.links = [] - #linksmembers = self.data.Links.Members - #linksmembers = self.data.links.Member + # linksmembers = self.data.Links.Members + # linksmembers = self.data.links.Member if float(mapping.redfish_version) < 1.00: - linksmembers = getattr(self.data, mapping.redfish_mapper.map_links()) - linksmembers = getattr(linksmembers, mapping.redfish_mapper.map_members()) + linksmembers = getattr( + self.data, mapping.redfish_mapper.map_links()) + linksmembers = getattr( + linksmembers, mapping.redfish_mapper.map_members()) else: - linksmembers = getattr(self.data, mapping.redfish_mapper.map_members()) + linksmembers = getattr( + self.data, mapping.redfish_mapper.map_members()) for link in linksmembers: - #self.links.append(getattr(link,'@odata.id')) - #self.links.append(getattr(link,'href')) - self.links.append(urljoin(self.url, getattr(link, mapping.redfish_mapper.map_links_ref()))) + # self.links.append(getattr(link,'@odata.id')) + # self.links.append(getattr(link,'href')) + self.links.append(urljoin( + self.url, getattr( + link, mapping.redfish_mapper.map_links_ref()))) config.logger.debug(self.links) @@ -302,18 +309,20 @@ class Managers(Device): def __init__(self, url, connection_parameters): super(Managers, self).__init__(url, connection_parameters) try: - # New proliant firmware now respects Redfish v1.00, so seems to correct below statement - # TODO : better handle exception and if possible support old firmware ? + # New proliant firmware now respects Redfish v1.00, so seems to + # correct below statement + # TODO : better handle exception and if possible support + # old firmware ? self.ethernet_interfaces_collection = \ EthernetInterfacesCollection( self.get_link_url('EthernetInterfaces'), connection_parameters) # Works on proliant, need to treat 095 vs 0.96 differences - #self.ethernet_interfaces_collection = EthernetInterfacesCollection( - # self.get_link_url('EthernetNICs'), - # connection_parameters - # ) + # self.ethernet_interfaces_collection = \ + # EthernetInterfacesCollection( + # self.get_link_url('EthernetNICs'), + # connection_parameters) except exception.InvalidRedfishContentException: # This is to avoid invalid content from the mockup self.ethernet_interfaces_collection = None @@ -359,7 +368,9 @@ class Managers(Device): try: for chassis in links.ManagerForChassis: - result = re.search(r'Chassis/(\w+)', chassis[mapping.redfish_mapper.map_links_ref(chassis)]) + result = re.search( + r'Chassis/(\w+)', + chassis[mapping.redfish_mapper.map_links_ref(chassis)]) chassis_list.append(result.group(1)) return chassis_list except AttributeError: @@ -413,7 +424,8 @@ class ManagersCollection(BaseCollection): self.managers_dict = {} for link in self.links: index = re.search(r'Managers/(\w+)', link) - self.managers_dict[index.group(1)] = Managers(link, connection_parameters) + self.managers_dict[index.group(1)] = Managers( + link, connection_parameters) class Systems(Device): @@ -674,14 +686,16 @@ class SystemsCollection(BaseCollection): for link in self.links: index = re.search(r'Systems/(\w+)', link) - self.systems_dict[index.group(1)] = Systems(link, connection_parameters) + self.systems_dict[index.group(1)] = Systems( + link, connection_parameters) class Bios(Base): '''Class to manage redfish Bios data.''' def __init__(self, url, connection_parameters): super(Bios, self).__init__(url, connection_parameters) - self.boot = Boot(re.findall('.+/Bios', url)[0] + '/Boot/Settings', connection_parameters) + self.boot = Boot(re.findall('.+/Bios', url)[0] + + '/Boot/Settings', connection_parameters) class Boot(Base): @@ -878,7 +892,8 @@ class ChassisCollection(BaseCollection): for link in self.links: index = re.search(r'Chassis/(\w+)', link) - self.chassis_dict[index.group(1)] = Chassis(link, connection_parameters) + self.chassis_dict[index.group(1)] = Chassis( + link, connection_parameters) class Chassis(Device):