From 34056eb00af667e6a66f69ff9a66946be8072906 Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 17 Feb 2016 19:08:30 +0100 Subject: [PATCH 01/16] Factorise redfish connection for client --- redfish-client/redfish-client | 66 +++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/redfish-client/redfish-client b/redfish-client/redfish-client index bcc34eb..74f73b0 100755 --- a/redfish-client/redfish-client +++ b/redfish-client/redfish-client @@ -87,7 +87,7 @@ class InventoryFile(object): def manager_incorect(self, exception): ''' Log and exit if manager name is incorect''' - logger.error('Incorect manager name : %s' % exception.args) + logger.error('Incorrect manager name : %s' % exception.args) sys.exit(1) def check_manager(self, manager_name): @@ -220,6 +220,27 @@ class RedfishClientException(Exception): if __name__ == '__main__': '''Main application redfish-client''' # Functions + def get_redfish_data(connection_parameters, check_SSL): + if not connection_parameters['login']: + simulator = True + enforceSSL = False + else: + simulator = False + enforceSSL = True + try: + redfish_data = redfish.connect(connection_parameters['url'], + connection_parameters['login'], + connection_parameters['password'], + verify_cert=check_SSL, + simulator=simulator, + enforceSSL=enforceSSL) + return(redfish_data) + except redfish.exception.RedfishException as e: + logger.error(str(e.message)) + sys.stderr.write(str(e.message)) + sys.stderr.write(str(e.advices)) + sys.exit(1) + def show_manager(all=False): '''Display manager info @@ -240,32 +261,7 @@ if __name__ == '__main__': print('\tLogin : {}'.format(info['login'])) print('\tPassword : {}'.format(info['password'])) - def get_manager_info(manager_name, check_SSL): - connection_parameters = inventory.get_manager_info(manager_name) - if not connection_parameters['login']: - simulator = True - enforceSSL = False - else: - simulator = False - enforceSSL = True - try: - print('Gathering data from manager, please wait...\n') - # TODO : Add a rotating star showing program is running ? - # Could be a nice exercice for learning python. :) - logger.info('Gathering data from manager') - remote_mgmt = redfish.connect(connection_parameters['url'], - connection_parameters['login'], - connection_parameters['password'], - verify_cert=check_SSL, - simulator=simulator, - enforceSSL=enforceSSL - ) - except redfish.exception.RedfishException as e: - logger.error(str(e.message)) - sys.stderr.write(str(e.message)) - sys.stderr.write(str(e.advices)) - sys.exit(1) - + def display_manager_info(redfish_data): # Display manager information using jinja2 template try: template = jinja2_env.get_template("manager_info.template") @@ -276,7 +272,7 @@ if __name__ == '__main__': % (e.message, jinja2_env.loader.searchpath[0])) sys.exit(1) - print(template.render(r=remote_mgmt)) + print(template.render(r=redfish_data)) ################################################################# # Main program @@ -406,7 +402,7 @@ if __name__ == '__main__': arguments['']) logger.debug(inventory.data) inventory.save() - if arguments['manager'] is True: + elif arguments['manager'] is True: logger.debug("Manager commands") if arguments['getinfo'] is True: logger.debug('getinfo command') @@ -417,10 +413,18 @@ if __name__ == '__main__': manager_name = arguments[''] # Check if the default section is available in our conf file inventory.check_manager(manager_name) + connection_parameters = inventory.get_manager_info(manager_name) + + print('Gathering data from manager, please wait...\n') + # TODO : Add a rotating star showing program is running ? + # Could be a nice exercice for learning python. :) + logger.info('Gathering data from manager') + if arguments['--insecure'] is True: - get_manager_info(manager_name, False) + redfish_data = get_redfish_data(connection_parameters, False) else: - get_manager_info(manager_name, True) + redfish_data = get_redfish_data(connection_parameters, True) + display_manager_info(redfish_data) logger.info("Client session terminated") sys.exit(0) From 333a6b297ede0448f711473d71879d528a155769 Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 17 Feb 2016 19:57:24 +0100 Subject: [PATCH 02/16] Prepare to introduce system and chassis getinfo --- redfish-client/redfish-client | 56 +++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/redfish-client/redfish-client b/redfish-client/redfish-client index 74f73b0..d42f2a2 100755 --- a/redfish-client/redfish-client +++ b/redfish-client/redfish-client @@ -12,6 +12,7 @@ redfish-client :: redfish-client [options] config show redfish-client [options] config showall redfish-client [options] manager getinfo [] + redfish-client [options] system getinfo [] redfish-client (-h | --help) redfish-client --version @@ -263,8 +264,15 @@ if __name__ == '__main__': def display_manager_info(redfish_data): # Display manager information using jinja2 template + render_template("manager_info.template") + + def display_system_info(redfish_data): + # Display system information using jinja2 template + render_template("system_info.template") + + def render_template(template): try: - template = jinja2_env.get_template("manager_info.template") + template = jinja2_env.get_template(template) except jinja2.exceptions.TemplateNotFound as e: print('Template "{}" not found in {}.' .format(e.message, jinja2_env.loader.searchpath[0])) @@ -402,29 +410,33 @@ if __name__ == '__main__': arguments['']) logger.debug(inventory.data) inventory.save() - elif arguments['manager'] is True: - logger.debug("Manager commands") - if arguments['getinfo'] is True: - logger.debug('getinfo command') - # If manager is not defined set it to 'default' - if not arguments['']: - manager_name = 'default' - else: - manager_name = arguments[''] - # Check if the default section is available in our conf file - inventory.check_manager(manager_name) - connection_parameters = inventory.get_manager_info(manager_name) + elif arguments['getinfo'] is True: + logger.debug('getinfo command') + # If manager is not defined set it to 'default' + if not arguments['']: + manager_name = 'default' + else: + manager_name = arguments[''] + # Check if the default section is available in our conf file + inventory.check_manager(manager_name) + connection_parameters = inventory.get_manager_info(manager_name) - print('Gathering data from manager, please wait...\n') - # TODO : Add a rotating star showing program is running ? - # Could be a nice exercice for learning python. :) - logger.info('Gathering data from manager') + print('Gathering data from manager, please wait...\n') + # TODO : Add a rotating star showing program is running ? + # Could be a nice exercice for learning python. :) + logger.info('Gathering data from manager') - if arguments['--insecure'] is True: - redfish_data = get_redfish_data(connection_parameters, False) - else: - redfish_data = get_redfish_data(connection_parameters, True) + if arguments['--insecure'] is True: + redfish_data = get_redfish_data(connection_parameters, False) + else: + redfish_data = get_redfish_data(connection_parameters, True) + if arguments['manager'] is True: + logger.debug("Manager commands") display_manager_info(redfish_data) - + elif arguments['system'] is True: + logger.debug("system commands") + display_system_info(redfish_data) + elif arguments['chassis'] is True: + pass logger.info("Client session terminated") sys.exit(0) From ebf968b4a4e06b0cdfc8899808ca4166eb6efa24 Mon Sep 17 00:00:00 2001 From: Uggla Date: Fri, 19 Feb 2016 18:24:03 +0100 Subject: [PATCH 03/16] Add system template --- redfish-client/templates/system_info.template | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 redfish-client/templates/system_info.template diff --git a/redfish-client/templates/system_info.template b/redfish-client/templates/system_info.template new file mode 100644 index 0000000..f80a8e8 --- /dev/null +++ b/redfish-client/templates/system_info.template @@ -0,0 +1,34 @@ +Redfish API version : {{ r.get_api_version() }} +{{ r.Root.get_name() }} + +Systems information : +===================== +{% for system_index in r.Systems.systems_dict | sort %} +{%- set system = r.Systems.systems_dict[system_index] %} +System id {{ system_index }}: +UUID : {{ system.get_uuid() }} +Type : {{ system.get_type() }} +Bios version : {{ system.get_bios_version() }} +State : {{ system.get_status() }} +{# +Ethernet Interface : +{%- if system.ethernet_interfaces_collection %} +{%- for ethernetinterface_index in system.ethernet_interfaces_collection.ethernet_interfaces_dict | sort %} +{%- set ei = system.ethernet_interfaces_collection.ethernet_interfaces_dict[ethernetinterface_index] %} + Ethernet Interface id {{ ethernetinterface_index }} : + {{ ei.get_name() }} + FQDN : {{ ei.get_fqdn() }} + Mac address : {{ ei.get_mac() }} + Address ipv4 : {{ ei.get_ipv4() | join(', ') }} + Address ipv6 : {{ ei.get_ipv6() | join(', ') }} +{%- endfor %} +{%- else %} + This system has no ethernet interface +{%- endif %} +Managed Chassis : + {{ system.get_managed_chassis() | join(', ') }} +Managed System : + {{ system.get_managed_systems() | join(', ') }} +---------------------------- +#} +{% endfor %} \ No newline at end of file From 983720d444d0388912fd1fa0238756fae8cd5660 Mon Sep 17 00:00:00 2001 From: Uggla Date: Mon, 7 Mar 2016 18:46:21 +0100 Subject: [PATCH 04/16] Introduce Device class to factorize common methods - Factorize common methods between System, Chassis, Manager. --- examples/simple-simulator.py | 9 +-- redfish/types.py | 118 +++++++++++++++++------------------ 2 files changed, 63 insertions(+), 64 deletions(-) diff --git a/examples/simple-simulator.py b/examples/simple-simulator.py index b431d67..3fd9e3a 100644 --- a/examples/simple-simulator.py +++ b/examples/simple-simulator.py @@ -6,12 +6,12 @@ from __future__ import print_function from __future__ import division from __future__ import absolute_import from future import standard_library -standard_library.install_aliases() import os import sys import json import redfish +standard_library.install_aliases() # Get $HOME environment. HOME = os.getenv('HOME') @@ -49,7 +49,8 @@ except redfish.exception.RedfishException as e: print("Redfish API version : {} \n".format(remote_mgmt.get_api_version())) print("UUID : {} \n".format(remote_mgmt.Root.get_api_UUID())) print("System 1 :\n") -print("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["1"].get_bios_version())) +print("Bios version : {}\n".format( + remote_mgmt.Systems.systems_dict["1"].get_bios_version())) print("System 2 :\n") -print("Bios version : {}\n".format(remote_mgmt.Systems.systems_dict["2"].get_parameter("SerialNumber"))) -#print remoteMgmt.get_api_link_to_server() +print("Bios version : {}\n".format( + remote_mgmt.Systems.systems_dict["2"].get_parameter("SerialNumber"))) diff --git a/redfish/types.py b/redfish/types.py index 48ea32a..9c8aebe 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -18,6 +18,8 @@ from . import mapping from . import exception standard_library.install_aliases() +standard_library.install_aliases() + # Global variable @@ -155,12 +157,54 @@ class BaseCollection(Base): config.logger.debug(self.links) +class Device(Base): + '''Abstract class to add common methods between devices + (Chassis, Servers, System). + ''' + def get_uuid(self): + '''Get device uuid + + :returns: device uuid or "Not available" + :rtype: string + + ''' + try: + return self.data.UUID + except AttributeError: + return "Not available" + + def get_status(self): + '''Get device status + + :returns: device status or "Not available" + :rtype: string + + ''' + try: + return self.data.Status.State + except AttributeError: + return "Not available" + + def get_type(self): + '''Get device type + + :returns: device type or "Not available" + :rtype: string + + ''' + try: + return self.data.SystemType + except AttributeError: + return "Not available" + + class Root(Base): '''Class to manage redfish Root data.''' def get_api_version(self): '''Return api version. - :returns: string -- version + :returns: api version + :rtype: string :raises: AttributeError ''' @@ -174,28 +218,21 @@ class Root(Base): return(version) def get_api_UUID(self): - '''Return UUID version. + '''Return api UUID. - :returns: string -- UUID + :returns: api UUID + :rtype: string ''' return self.data.UUID - def get_api_link_to_server(self): - '''Return api link to server. - - :returns: string -- path - - ''' - return getattr(self.root.Links.Systems, '@odata.id') - class SessionService(Base): '''Class to manage redfish SessionService data.''' pass -class Managers(Base): +class Managers(Device): '''Class to manage redfish Managers.''' def __init__(self, url, connection_parameters): super(Managers, self).__init__(url, connection_parameters) @@ -233,39 +270,6 @@ class Managers(Base): # This is the case with the mockup for manager 2 and 3 return "Not available" - def get_type(self): - '''Get manager type - - :returns: string -- manager type or "Not available" - - ''' - try: - return self.data.ManagerType - except AttributeError: - return "Not available" - - def get_uuid(self): - '''Get manager type - - :returns: string -- manager uuid or "Not available" - - ''' - try: - return self.data.UUID - except AttributeError: - return "Not available" - - def get_status(self): - '''Get manager status - - :returns: string -- manager status or "Not available" - - ''' - try: - return self.data.Status.State - except AttributeError: - return "Not available" - def get_managed_chassis(self): '''Get managed chassis ids by the manager @@ -331,7 +335,7 @@ class ManagersCollection(BaseCollection): self.managers_dict[index.group(1)] = Managers(link, connection_parameters) -class Systems(Base): +class Systems(Device): '''Class to manage redfish Systems data.''' # TODO : Need to discuss with Bruno the required method. # Also to check with the ironic driver requirement. @@ -366,32 +370,26 @@ class Systems(Base): def get_bios_version(self): '''Get bios version of the system. - :returns: string -- bios version + :returns: bios version or "Not available" + :rtype: string ''' try: - # Returned by proliant - return self.data.Bios.Current.VersionString - except: - # Returned by mockup. - # Hopefully this kind of discrepencies will be fixed with - # Redfish 1.0 (August) return self.data.BiosVersion + except AttributeError: + return "Not available" def get_serial_number(self): '''Get serial number of the system. - :returns: string -- serial number + :returns: serial number or "Not available" + :rtype: string ''' try: - # Returned by proliant return self.data.SerialNumber - except: - # Returned by mockup. - # Hopefully this kind of discrepencies will be fixed with - # Redfish 1.0 (August) - return '' + except AttributeError: + return "Not available" def get_power(self): '''Get power status of the system. From 25a5f1228770364b605b70b29ea0bf0da6c46d86 Mon Sep 17 00:00:00 2001 From: Uggla Date: Sun, 20 Mar 2016 22:16:44 +0100 Subject: [PATCH 05/16] Improve systems class and template - Add new functions. --- redfish-client/templates/system_info.template | 9 + redfish/types.py | 169 ++++++++++++++++-- 2 files changed, 164 insertions(+), 14 deletions(-) diff --git a/redfish-client/templates/system_info.template b/redfish-client/templates/system_info.template index f80a8e8..60a8cfe 100644 --- a/redfish-client/templates/system_info.template +++ b/redfish-client/templates/system_info.template @@ -8,8 +8,17 @@ Systems information : System id {{ system_index }}: UUID : {{ system.get_uuid() }} Type : {{ system.get_type() }} +Manufacturer : {{ system.get_manufacturer() }} +Model : {{ system.get_model() }} +SKU : {{ system.get_sku() }} +Serial : {{ system.get_serial_number() }} +Hostname : {{ system.get_hostname() }} Bios version : {{ system.get_bios_version() }} State : {{ system.get_status() }} +Power : {{ system.get_power() }} +Description : {{ system.get_description() }} +Chassis : {{ system.get_chassis() | join(', ') }} +Managers : {{ system.get_managers() | join(', ') }} {# Ethernet Interface : {%- if system.ethernet_interfaces_collection %} diff --git a/redfish/types.py b/redfish/types.py index 9c8aebe..99df185 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -177,11 +177,11 @@ class Device(Base): '''Get device status :returns: device status or "Not available" - :rtype: string + :rtype: dict ''' try: - return self.data.Status.State + return self.data.Status except AttributeError: return "Not available" @@ -197,6 +197,78 @@ class Device(Base): except AttributeError: return "Not available" + def get_model(self): + '''Get device model + + :returns: device model or "Not available" + :rtype: string + + ''' + try: + return self.data.Model + except AttributeError: + return "Not available" + + def get_manufacturer(self): + '''Get device manufacturer + + :returns: device manufacturer or "Not available" + :rtype: string + + ''' + try: + return self.data.Manufacturer + except AttributeError: + return "Not available" + + def get_serial_number(self): + '''Get serial number of the device. + + :returns: serial number or "Not available" + :rtype: string + + ''' + try: + return self.data.SerialNumber + except AttributeError: + return "Not available" + + def get_asset_tag(self): + '''Get asset tag of the device. + + :returns: asset tag or "Not available" + :rtype: string + + ''' + try: + return self.data.AssetTag + except AttributeError: + return "Not available" + + def get_sku(self): + '''Get sku number of the device. + + :returns: sku number or "Not available" + :rtype: string + + ''' + try: + return self.data.SKU + except AttributeError: + return "Not available" + + def get_part_number(self): + '''Get part number of the device. + + :returns: part number or "Not available" + :rtype: string + + ''' + try: + return self.data.PartNumber + except AttributeError: + return "Not available" + class Root(Base): '''Class to manage redfish Root data.''' @@ -273,7 +345,8 @@ class Managers(Device): def get_managed_chassis(self): '''Get managed chassis ids by the manager - :returns: list -- chassis ids or "Not available" + :returns: chassis ids or "Not available" + :rtype: list ''' chassis_list = [] @@ -290,7 +363,8 @@ class Managers(Device): def get_managed_systems(self): '''Get managed systems ids by the manager - :returns: list -- chassis ids or "Not available" + :returns: systems ids or "Not available" + :rtype: list ''' systems_list = [] @@ -298,7 +372,9 @@ class Managers(Device): try: for systems in links.ManagerForServers: - result = re.search(r'Systems/(\w+)', systems[mapping.redfish_mapper.map_links_ref(systems)]) + result = re.search( + r'Systems/(\w+)', + systems[mapping.redfish_mapper.map_links_ref(systems)]) systems_list.append(result.group(1)) return systems_list except AttributeError: @@ -370,7 +446,7 @@ class Systems(Device): def get_bios_version(self): '''Get bios version of the system. - :returns: bios version or "Not available" + :returns: bios version or "Not available" :rtype: string ''' @@ -379,28 +455,93 @@ class Systems(Device): except AttributeError: return "Not available" - def get_serial_number(self): - '''Get serial number of the system. + def get_hostname(self): + '''Get hostname of the system. - :returns: serial number or "Not available" + :returns: hostname or "Not available" :rtype: string ''' try: - return self.data.SerialNumber + return self.data.HostName + except AttributeError: + return "Not available" + + def get_indicatorled(self): + '''Get indicatorled of the system. + + :returns: indicatorled status or "Not available" + :rtype: string + + ''' + try: + return self.data.IndicatorLED except AttributeError: return "Not available" def get_power(self): '''Get power status of the system. - :returns: string -- power status or NULL if there is an issue + :returns: system power state or "Not available" + :rtype: string ''' try: - return self.data.Power - except: - return '' + return self.data.PowerState + except AttributeError: + return "Not available" + + def get_description(self): + '''Get description of the system. + + :returns: system description or "Not available" + :rtype: string + + ''' + try: + return self.data.Description + except AttributeError: + return "Not available" + + def get_chassis(self): + '''Get chassis ids used by the system + + :returns: chassis ids or "Not available" + :rtype: list + + ''' + chassis_list = [] + links = getattr(self.data, mapping.redfish_mapper.map_links(self.data)) + + try: + for chassis in links.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: + return "Not available" + + def get_managers(self): + '''Get manager ids used by the system + + :returns: managers ids or "Not available" + :rtype: list + + ''' + managers_list = [] + links = getattr(self.data, mapping.redfish_mapper.map_links(self.data)) + + try: + for manager in links.ManagedBy: + result = re.search( + r'Managers/(\w+)', + manager[mapping.redfish_mapper.map_links_ref(manager)]) + managers_list.append(result.group(1)) + return managers_list + except AttributeError: + return "Not available" def set_parameter_json(self, value): '''Generic function to set any system parameter using json structure From cf12504b66bbacf31c69d08c6744bc509a4a98f2 Mon Sep 17 00:00:00 2001 From: Uggla Date: Sun, 20 Mar 2016 23:32:31 +0100 Subject: [PATCH 06/16] Add ProcessorsCollection and Processors classes --- redfish-client/templates/system_info.template | 26 +++-- redfish/types.py | 106 ++++++++++++++++++ 2 files changed, 124 insertions(+), 8 deletions(-) diff --git a/redfish-client/templates/system_info.template b/redfish-client/templates/system_info.template index 60a8cfe..07d8e47 100644 --- a/redfish-client/templates/system_info.template +++ b/redfish-client/templates/system_info.template @@ -14,12 +14,27 @@ SKU : {{ system.get_sku() }} Serial : {{ system.get_serial_number() }} Hostname : {{ system.get_hostname() }} Bios version : {{ system.get_bios_version() }} -State : {{ system.get_status() }} +CPU number : {{ system.get_cpucount() }} +CPU model : {{ system.get_cpumodel() }} + +{%- if system.processors_collection %} +CPU details : + {%- for cpu_index in system.processors_collection.processors_dict | sort %} + {%- set cpu = system.processors_collection.processors_dict[cpu_index] %} + Processor id {{ cpu_index }} : + Speed : {{ cpu.get_speed() }} + Cores : {{ cpu.get_cores() }} + Threads : {{ cpu.get_threads() }} + {% endfor %} +{%- endif %} +Available memory : {{ system.get_memory() }} +Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }} Power : {{ system.get_power() }} Description : {{ system.get_description() }} Chassis : {{ system.get_chassis() | join(', ') }} Managers : {{ system.get_managers() | join(', ') }} -{# +IndicatorLED : {{ system.get_indicatorled() }} + Ethernet Interface : {%- if system.ethernet_interfaces_collection %} {%- for ethernetinterface_index in system.ethernet_interfaces_collection.ethernet_interfaces_dict | sort %} @@ -34,10 +49,5 @@ Ethernet Interface : {%- else %} This system has no ethernet interface {%- endif %} -Managed Chassis : - {{ system.get_managed_chassis() | join(', ') }} -Managed System : - {{ system.get_managed_systems() | join(', ') }} ----------------------------- -#} +-------------------------------------------------------------------------------- {% endfor %} \ No newline at end of file diff --git a/redfish/types.py b/redfish/types.py index 99df185..84e9f85 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -423,6 +423,24 @@ class Systems(Device): except: pass + try: + self.ethernet_interfaces_collection = \ + EthernetInterfacesCollection( + self.get_link_url('EthernetInterfaces'), + connection_parameters) + except AttributeError: + # This means we don't have EthernetInterfaces + self.ethernet_interfaces_collection = None + + try: + self.processors_collection = \ + ProcessorsCollection( + self.get_link_url('Processors'), + connection_parameters) + except AttributeError: + # This means we don't have Processors detailed data + self.processors_collection = None + def reset_system(self): '''Force reset of the system. @@ -503,6 +521,42 @@ class Systems(Device): except AttributeError: return "Not available" + def get_cpucount(self): + '''Get the number of cpu in the system. + + :returns: number of cpu or "Not available" + :rtype: string + + ''' + try: + return self.data.ProcessorSummary.Count + except AttributeError: + return "Not available" + + def get_cpumodel(self): + '''Get the cpu model available in the system. + + :returns: cpu model or "Not available" + :rtype: string + + ''' + try: + return self.data.ProcessorSummary.Model + except AttributeError: + return "Not available" + + def get_memory(self): + '''Get the memory available in the system. + + :returns: memory available or "Not available" + :rtype: string + + ''' + try: + return self.data.MemorySummary.TotalSystemMemoryGiB + except AttributeError: + return "Not available" + def get_chassis(self): '''Get chassis ids used by the system @@ -690,3 +744,55 @@ class EthernetInterfaces(Base): except AttributeError: return "Not available" + +class ProcessorsCollection(BaseCollection): + '''Class to manage redfish ProcessorsColkection data.''' + def __init__(self, url, connection_parameters): + super(ProcessorsCollection, + self).__init__(url, connection_parameters) + + self.processors_dict = {} + + for link in self.links: + index = re.search(r'Processors/(\w+)', link) + self.processors_dict[index.group(1)] = \ + Processors(link, connection_parameters) + + +class Processors(Base): + '''Class to manage redfish Processors.''' + def get_speed(self): + '''Get processor speed + + :returns: processor speed or "Not available" + :rtype: string + + ''' + try: + return self.data.MaxSpeedMHz + except AttributeError: + return "Not available" + + def get_cores(self): + '''Get processor cores number + + :returns: cores number or "Not available" + :rtype: string + + ''' + try: + return self.data.TotalCores + except AttributeError: + return "Not available" + + def get_threads(self): + '''Get processor threads number + + :returns: threads number or "Not available" + :rtype: string + + ''' + try: + return self.data.TotalThreads + except AttributeError: + return "Not available" From 3a60af5373f2c1b0f90d08f542f6eae40cd466b0 Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 31 Mar 2016 22:47:10 +0200 Subject: [PATCH 07/16] Add SimpleStorageCollection and SimpleStorage classes --- .../templates/manager_info.template | 2 +- redfish-client/templates/system_info.template | 15 ++++++ redfish/types.py | 52 ++++++++++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/redfish-client/templates/manager_info.template b/redfish-client/templates/manager_info.template index d3f0d22..fb409f0 100644 --- a/redfish-client/templates/manager_info.template +++ b/redfish-client/templates/manager_info.template @@ -9,7 +9,7 @@ Manager id {{ manager_index }}: UUID : {{ manager.get_uuid() }} Type : {{ manager.get_type() }} Firmware version : {{ manager.get_firmware_version() }} -State : {{ manager.get_status() }} +Status : State : {{ manager.get_status().Health }} / Health : {{ manager.get_status().Health }} Ethernet Interface : {%- if manager.ethernet_interfaces_collection %} {%- for ethernetinterface_index in manager.ethernet_interfaces_collection.ethernet_interfaces_dict | sort %} diff --git a/redfish-client/templates/system_info.template b/redfish-client/templates/system_info.template index 07d8e47..3fe739b 100644 --- a/redfish-client/templates/system_info.template +++ b/redfish-client/templates/system_info.template @@ -49,5 +49,20 @@ Ethernet Interface : {%- else %} This system has no ethernet interface {%- endif %} + +Simple Storage : +{%- if system.simple_storage_collection %} +{%- for simplestorage_index in system.simple_storage_collection.simple_storage_dict | sort %} +{%- set ss = system.simple_storage_collection.simple_storage_dict[simplestorage_index] %} + Simple Storage id {{ simplestorage_index }} : + {{ ss.get_name() }} + Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }} + {%- for dev in ss.get_devices() %} + Device id {{ loop.index }} : {{ dev.Name }} {{ dev.Manufacturer }} {{ dev.Model }} + {%- endfor %} +{%- endfor %} +{%- else %} + This system has no simple storage +{%- endif %} -------------------------------------------------------------------------------- {% endfor %} \ No newline at end of file diff --git a/redfish/types.py b/redfish/types.py index 84e9f85..e8157a7 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -441,6 +441,15 @@ class Systems(Device): # This means we don't have Processors detailed data self.processors_collection = None + try: + self.simple_storage_collection = \ + SimpleStorageCollection( + self.get_link_url('SimpleStorage'), + connection_parameters) + except AttributeError: + # This means we don't have Processors detailed data + self.simple_storage_collection = None + def reset_system(self): '''Force reset of the system. @@ -746,7 +755,7 @@ class EthernetInterfaces(Base): class ProcessorsCollection(BaseCollection): - '''Class to manage redfish ProcessorsColkection data.''' + '''Class to manage redfish ProcessorsCollection data.''' def __init__(self, url, connection_parameters): super(ProcessorsCollection, self).__init__(url, connection_parameters) @@ -796,3 +805,44 @@ class Processors(Base): return self.data.TotalThreads except AttributeError: return "Not available" + + +class SimpleStorageCollection(BaseCollection): + '''Class to manage redfish SimpleStorageCollection data.''' + def __init__(self, url, connection_parameters): + super(SimpleStorageCollection, + self).__init__(url, connection_parameters) + + self.simple_storage_dict = {} + + for link in self.links: + index = re.search(r'SimpleStorage/(\w+)', link) + self.simple_storage_dict[index.group(1)] = \ + SimpleStorage(link, connection_parameters) + + +class SimpleStorage(Base): + '''Class to manage redfish SimpleStorage''' + def get_status(self): + '''Get storage status + + :returns: storage status or "Not available" + :rtype: dict + + ''' + try: + return self.data.Status + except AttributeError: + return "Not available" + + def get_devices(self): + '''Get storage devices + + :returns: storage devices or "Not available" + :rtype: list of dict + + ''' + try: + return self.data.Devices + except AttributeError: + return "Not available" From 14314c99ab830e0d876b249876d2f4884d00e5f7 Mon Sep 17 00:00:00 2001 From: Uggla Date: Thu, 31 Mar 2016 23:28:59 +0200 Subject: [PATCH 08/16] Add ChassisCollection, Chassis classes and chassis template --- redfish-client/redfish-client | 8 ++- .../templates/chassis_info.template | 72 +++++++++++++++++++ redfish-client/templates/system_info.template | 2 +- redfish/main.py | 4 +- redfish/types.py | 39 ++++++++++ 5 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 redfish-client/templates/chassis_info.template diff --git a/redfish-client/redfish-client b/redfish-client/redfish-client index d42f2a2..038120f 100755 --- a/redfish-client/redfish-client +++ b/redfish-client/redfish-client @@ -12,6 +12,7 @@ redfish-client :: redfish-client [options] config show redfish-client [options] config showall redfish-client [options] manager getinfo [] + redfish-client [options] chassis getinfo [] redfish-client [options] system getinfo [] redfish-client (-h | --help) redfish-client --version @@ -266,6 +267,10 @@ if __name__ == '__main__': # Display manager information using jinja2 template render_template("manager_info.template") + def display_chassis_info(redfish_data): + # Display system information using jinja2 template + render_template("chassis_info.template") + def display_system_info(redfish_data): # Display system information using jinja2 template render_template("system_info.template") @@ -437,6 +442,7 @@ if __name__ == '__main__': logger.debug("system commands") display_system_info(redfish_data) elif arguments['chassis'] is True: - pass + logger.debug("chassis commands") + display_chassis_info(redfish_data) logger.info("Client session terminated") sys.exit(0) diff --git a/redfish-client/templates/chassis_info.template b/redfish-client/templates/chassis_info.template new file mode 100644 index 0000000..704e70f --- /dev/null +++ b/redfish-client/templates/chassis_info.template @@ -0,0 +1,72 @@ +Redfish API version : {{ r.get_api_version() }} +{{ r.Root.get_name() }} + +Chassis information : +===================== +{% for chassis_index in r.Chassis.chassis_dict | sort %} +{%- set chassis = r.Chassis.chassis_dict[chassis_index] %} +Chassis id {{ chassis_index }}: +Manufacturer : {{ chassis.get_manufacturer() }} +Model : {{ chassis.get_model() }} +Chassis Type : {{ chassis.get_chassis_type() }} +PartNumber : {{ chassis.get_part_number() }} +SKU : {{ chassis.get_sku() }} +Serial : {{ chassis.get_serial_number() }} +AssetTag : {{ chassis.get_asset_tag() }} +Status : State : {{ chassis.get_status().Health }} / Health : {{ chassis.get_status().Health }} +{# +Hostname : {{ system.get_hostname() }} +Bios version : {{ system.get_bios_version() }} +CPU number : {{ system.get_cpucount() }} +CPU model : {{ system.get_cpumodel() }} + +{%- if system.processors_collection %} +CPU details : + {%- for cpu_index in system.processors_collection.processors_dict | sort %} + {%- set cpu = system.processors_collection.processors_dict[cpu_index] %} + Processor id {{ cpu_index }} : + Speed : {{ cpu.get_speed() }} + Cores : {{ cpu.get_cores() }} + Threads : {{ cpu.get_threads() }} + {% endfor %} +{%- endif %} +Available memory : {{ system.get_memory() }} +Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }} +Power : {{ system.get_power() }} +Description : {{ system.get_description() }} +Chassis : {{ system.get_chassis() | join(', ') }} +Managers : {{ system.get_managers() | join(', ') }} +IndicatorLED : {{ system.get_indicatorled() }} + +Ethernet Interface : +{%- if system.ethernet_interfaces_collection %} +{%- for ethernetinterface_index in system.ethernet_interfaces_collection.ethernet_interfaces_dict | sort %} +{%- set ei = system.ethernet_interfaces_collection.ethernet_interfaces_dict[ethernetinterface_index] %} + Ethernet Interface id {{ ethernetinterface_index }} : + {{ ei.get_name() }} + FQDN : {{ ei.get_fqdn() }} + Mac address : {{ ei.get_mac() }} + Address ipv4 : {{ ei.get_ipv4() | join(', ') }} + Address ipv6 : {{ ei.get_ipv6() | join(', ') }} +{%- endfor %} +{%- else %} + This system has no ethernet interface +{%- endif %} + +Simple Storage : +{%- if system.simple_storage_collection %} +{%- for simplestorage_index in system.simple_storage_collection.simple_storage_dict | sort %} +{%- set ss = system.simple_storage_collection.simple_storage_dict[simplestorage_index] %} + Simple Storage id {{ simplestorage_index }} : + {{ ss.get_name() }} + Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }} + {%- for dev in ss.get_devices() %} + Device id {{ loop.index }} : {{ dev.Name }} {{ dev.Manufacturer }} {{ dev.Model }} + {%- endfor %} +{%- endfor %} +{%- else %} + This system has no simple storage +{%- endif %} +-------------------------------------------------------------------------------- +#} +{% endfor %} \ No newline at end of file diff --git a/redfish-client/templates/system_info.template b/redfish-client/templates/system_info.template index 3fe739b..6e40a63 100644 --- a/redfish-client/templates/system_info.template +++ b/redfish-client/templates/system_info.template @@ -27,7 +27,7 @@ CPU details : Threads : {{ cpu.get_threads() }} {% endfor %} {%- endif %} -Available memory : {{ system.get_memory() }} +Available memory : {{ system.get_memory() }} GB Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }} Power : {{ system.get_power() }} Description : {{ system.get_description() }} diff --git a/redfish/main.py b/redfish/main.py index 094d10a..0890567 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -254,7 +254,9 @@ class RedfishConnection(object): self.connection_parameters ) -# self.Chassis + self.Chassis = types.ChassisCollection(self.Root.get_link_url("Chassis"), + self.connection_parameters + ) # self.EventService # self.AccountService diff --git a/redfish/types.py b/redfish/types.py index e8157a7..54f3023 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -846,3 +846,42 @@ class SimpleStorage(Base): return self.data.Devices except AttributeError: return "Not available" + + +class ChassisCollection(BaseCollection): + '''Class to manage redfish ChassisCollection data.''' + def __init__(self, url, connection_parameters): + super(ChassisCollection, self).__init__(url, connection_parameters) + + self.chassis_dict = {} + + for link in self.links: + index = re.search(r'Chassis/(\w+)', link) + self.chassis_dict[index.group(1)] = Chassis(link, connection_parameters) + + +class Chassis(Device): + '''Class to manage redfish Chassis data.''' + def __init__(self, url, connection_parameters): + '''Class constructor''' + super(Chassis, self).__init__(url, connection_parameters) + +# try: +# self.ethernet_interfaces_collection = \ +# EthernetInterfacesCollection( +# self.get_link_url('EthernetInterfaces'), +# connection_parameters) +# except AttributeError: +# # This means we don't have EthernetInterfaces +# self.ethernet_interfaces_collection = None + def get_chassis_type(self): + '''Get chassis type + + :returns: chassis type or "Not available" + :rtype: string + + ''' + try: + return self.data.ChassisType + except AttributeError: + return "Not available" From 6b4450565ec14ba7ffabed0e4a7b8c4a5c38f8ed Mon Sep 17 00:00:00 2001 From: Uggla Date: Sun, 3 Apr 2016 19:12:52 +0200 Subject: [PATCH 09/16] Fix #53, handle MacAddress and MACAddress keys - Proliant firmware uses MacAddress but the standard is MACAddress. --- redfish/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/redfish/types.py b/redfish/types.py index 54f3023..d13ab0f 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -702,9 +702,13 @@ class EthernetInterfaces(Base): ''' try: + # Proliant firmware seems to not follow redfish systax return self.data.MacAddress except AttributeError: - return "Not available" + try: + return self.data.MACAddress + except AttributeError: + return "Not available" def get_fqdn(self): '''Get EthernetInterface fqdn From a287469ff2d524bcea63dead403b6e3316d74eb1 Mon Sep 17 00:00:00 2001 From: Uggla Date: Sun, 3 Apr 2016 21:39:29 +0200 Subject: [PATCH 10/16] Sort inventory entries diplayed with showall --- redfish-client/redfish-client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redfish-client/redfish-client b/redfish-client/redfish-client index 038120f..cd88f02 100755 --- a/redfish-client/redfish-client +++ b/redfish-client/redfish-client @@ -255,7 +255,7 @@ if __name__ == '__main__': if(not inventory.get_managers()): print("None") else: - for manager in inventory.get_managers(): + for manager in sorted(inventory.get_managers()): print(manager) if all is True: info = inventory.get_manager_info(manager) From d940d11af4ad6267298fa1937d19c281df18a64d Mon Sep 17 00:00:00 2001 From: Uggla Date: Mon, 4 Apr 2016 00:05:10 +0200 Subject: [PATCH 11/16] Add Thermal, Power classes and review get_type() --- .../templates/chassis_info.template | 21 +++- redfish/types.py | 98 +++++++++++++++---- 2 files changed, 97 insertions(+), 22 deletions(-) diff --git a/redfish-client/templates/chassis_info.template b/redfish-client/templates/chassis_info.template index 704e70f..28acd89 100644 --- a/redfish-client/templates/chassis_info.template +++ b/redfish-client/templates/chassis_info.template @@ -8,12 +8,31 @@ Chassis information : Chassis id {{ chassis_index }}: Manufacturer : {{ chassis.get_manufacturer() }} Model : {{ chassis.get_model() }} -Chassis Type : {{ chassis.get_chassis_type() }} +Chassis Type : {{ chassis.get_type() }} PartNumber : {{ chassis.get_part_number() }} SKU : {{ chassis.get_sku() }} Serial : {{ chassis.get_serial_number() }} AssetTag : {{ chassis.get_asset_tag() }} Status : State : {{ chassis.get_status().Health }} / Health : {{ chassis.get_status().Health }} +{%- if chassis.thermal %} +Temperatures : +{%- if chassis.thermal.get_temperatures() == 'Not available' %} + Not available +{%- else %} +{%- for sensor, temp in chassis.thermal.get_temperatures().items() | sort %} + {{ sensor }} : {{ temp }} +{%- endfor %} +{%- endif %} +Fans : +{%- if chassis.thermal.get_fans() == 'Not available' %} + Not available +{%- else %} +{%- for fan, rpm in chassis.thermal.get_fans().items() | sort %} + {{ fan }} : {{ rpm }} +{%- endfor %} +{%- endif %} +{%- endif %} + {# Hostname : {{ system.get_hostname() }} Bios version : {{ system.get_bios_version() }} diff --git a/redfish/types.py b/redfish/types.py index d13ab0f..304117f 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -185,18 +185,6 @@ class Device(Base): except AttributeError: return "Not available" - def get_type(self): - '''Get device type - - :returns: device type or "Not available" - :rtype: string - - ''' - try: - return self.data.SystemType - except AttributeError: - return "Not available" - def get_model(self): '''Get device model @@ -329,6 +317,18 @@ class Managers(Device): # This means we don't have EthernetInterfaces self.ethernet_interfaces_collection = None + def get_type(self): + '''Get manager type + + :returns: manager type or "Not available" + :rtype: string + + ''' + try: + return self.data.ManagerType + except AttributeError: + return "Not available" + def get_firmware_version(self): '''Get firmware version of the manager @@ -566,6 +566,18 @@ class Systems(Device): except AttributeError: return "Not available" + def get_type(self): + '''Get system type + + :returns: system type or "Not available" + :rtype: string + + ''' + try: + return self.data.SystemType + except AttributeError: + return "Not available" + def get_chassis(self): '''Get chassis ids used by the system @@ -870,15 +882,19 @@ class Chassis(Device): '''Class constructor''' super(Chassis, self).__init__(url, connection_parameters) -# try: -# self.ethernet_interfaces_collection = \ -# EthernetInterfacesCollection( -# self.get_link_url('EthernetInterfaces'), -# connection_parameters) -# except AttributeError: -# # This means we don't have EthernetInterfaces -# self.ethernet_interfaces_collection = None - def get_chassis_type(self): + try: + self.thermal = Thermal(self.get_link_url('Thermal'), + connection_parameters) + except AttributeError: + self.thermal = None + + try: + self.power = Power(self.get_link_url('Power'), + connection_parameters) + except AttributeError: + self.Power = None + + def get_type(self): '''Get chassis type :returns: chassis type or "Not available" @@ -889,3 +905,43 @@ class Chassis(Device): return self.data.ChassisType except AttributeError: return "Not available" + + +class Thermal(Base): + '''Class to manage redfish Thermal data.''' + def get_temperatures(self): + '''Get chassis sensors name and temparature + + :returns: chassis sensor and temperature + :rtype: dict + + ''' + temperatures = {} + + try: + for sensor in self.data.Temperatures: + temperatures[sensor.Name] = sensor.ReadingCelsius + return temperatures + except AttributeError: + return "Not available" + + def get_fans(self): + '''Get chassis fan name and rpm + + :returns: chassis fan and rpm + :rtype: dict + + ''' + fans = {} + + try: + for fan in self.data.Fans: + fans[fan.FanName] = fan.ReadingRPM + return fans + except AttributeError: + return "Not available" + + +class Power(Base): + '''Class to manage redfish Power data.''' + pass From 45fdc2819e8a69814ccdd3163766bf1851634023 Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 6 Apr 2016 11:38:10 +0200 Subject: [PATCH 12/16] Remove socket module finally not needed --- redfish/main.py | 1 - 1 file changed, 1 deletion(-) diff --git a/redfish/main.py b/redfish/main.py index 0890567..a080129 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -125,7 +125,6 @@ standard_library.install_aliases() from builtins import object import json -import socket from urllib.parse import urlparse, urljoin, urlunparse import requests from . import config From 92d4aa880a17772b0be29661db0b138f88d9c13d Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 6 Apr 2016 13:46:58 +0200 Subject: [PATCH 13/16] Improve debug readability - Pretty print data returned, so humans can read them. - Cosmetic fixes. --- redfish/main.py | 3 --- redfish/types.py | 9 +++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/redfish/main.py b/redfish/main.py index a080129..9bdf5f0 100644 --- a/redfish/main.py +++ b/redfish/main.py @@ -205,9 +205,6 @@ class RedfishConnection(object): "this is insecure and can allow" + " a man in the middle attack") - # Show redfish standard headers - config.logger.debug(self.connection_parameters.headers) - config.logger.debug("Root url : %s", self.connection_parameters.rooturl) self.Root = types.Root(self.connection_parameters.rooturl, diff --git a/redfish/types.py b/redfish/types.py index 304117f..0ee1b35 100644 --- a/redfish/types.py +++ b/redfish/types.py @@ -32,7 +32,12 @@ class Base(object): self.url = url self.api_url = tortilla.wrap(url, debug=config.TORTILLADEBUG) - config.logger.debug(connection_parameters.headers) + config.logger.debug( + "------------------------------------------------------------") + config.logger.debug("Url: %s" % url) + config.logger.debug("Header: %s" % connection_parameters.headers) + config.logger.debug( + "------------------------------------------------------------") try: self.data = self.api_url.get( @@ -50,7 +55,7 @@ class Base(object): 'Ivalid content : Content does not appear to be a valid ' + \ 'Redfish json\n' raise exception.InvalidRedfishContentException(msg) - config.logger.debug(self.data) + config.logger.debug(pprint.PrettyPrinter(indent=4).pformat(self.data)) def get_link_url(self, link_type): '''Need to be explained. From 2f40ce5d24b087d2002c1a3fe5dc5c5bc3d43078 Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 6 Apr 2016 15:20:35 +0200 Subject: [PATCH 14/16] PEP8 fixes --- examples/simple-proliant.py | 73 ++++++++++++++++++++--------------- redfish-client/redfish-client | 2 +- redfish/__init__.py | 2 +- redfish/config.py | 22 ++++++----- redfish/exception.py | 5 ++- redfish/main.py | 62 ++++++++++++++--------------- redfish/mapping.py | 15 +++---- redfish/types.py | 57 +++++++++++++++++---------- 8 files changed, 134 insertions(+), 104 deletions(-) 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): From dae27b4a7252190b4f071eb5aacfa1a37668509d Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 6 Apr 2016 15:33:26 +0200 Subject: [PATCH 15/16] Fix missing home message --- redfish/config.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/redfish/config.py b/redfish/config.py index ccd9dc7..ffc9aee 100644 --- a/redfish/config.py +++ b/redfish/config.py @@ -29,10 +29,8 @@ 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(REDFISH_HOME)) + print(' using: mkdir -p {}'.format(REDFISH_HOME)) sys.exit(1) REDFISH_LOGFILE = os.path.join(REDFISH_HOME, "python-redfish.log") From f1dfdf4b7b2338017d11cf78574e8c7bba044fb1 Mon Sep 17 00:00:00 2001 From: Uggla Date: Wed, 6 Apr 2016 15:50:57 +0200 Subject: [PATCH 16/16] Nicer import --- redfish/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/redfish/__init__.py b/redfish/__init__.py index 724f97e..ec1f05c 100644 --- a/redfish/__init__.py +++ b/redfish/__init__.py @@ -19,7 +19,7 @@ from __future__ import absolute_import from future import standard_library import pbr.version -from redfish.main import * +from redfish.main import connect standard_library.install_aliases() try: