Merge pull request #65 from uggla/systemandchassis

System and chassis
This commit is contained in:
Bruno Cornec 2016-04-06 18:59:53 +02:00
commit e958c08fbd
12 changed files with 836 additions and 229 deletions

View File

@ -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()

View File

@ -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")))

View File

@ -12,6 +12,8 @@ redfish-client ::
redfish-client [options] config show
redfish-client [options] config showall
redfish-client [options] manager getinfo [<manager_name>]
redfish-client [options] chassis getinfo [<manager_name>]
redfish-client [options] system getinfo [<manager_name>]
redfish-client (-h | --help)
redfish-client --version
@ -36,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
@ -50,6 +51,7 @@ import configparser
import jinja2
import requests.packages.urllib3
import redfish
standard_library.install_aliases()
class InventoryFile(object):
@ -87,7 +89,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 +222,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
@ -232,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)
@ -240,35 +263,21 @@ 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
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")
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]))
@ -276,7 +285,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,21 +415,34 @@ if __name__ == '__main__':
arguments['<changed_value>'])
logger.debug(inventory.data)
inventory.save()
if 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>']:
manager_name = 'default'
else:
manager_name = arguments['<manager_name>']
# Check if the default section is available in our conf file
inventory.check_manager(manager_name)
if arguments['--insecure'] is True:
get_manager_info(manager_name, False)
else:
get_manager_info(manager_name, True)
elif arguments['getinfo'] is True:
logger.debug('getinfo command')
# If manager is not defined set it to 'default'
if not arguments['<manager_name>']:
manager_name = 'default'
else:
manager_name = arguments['<manager_name>']
# 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:
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:
logger.debug("chassis commands")
display_chassis_info(redfish_data)
logger.info("Client session terminated")
sys.exit(0)

View File

@ -0,0 +1,91 @@
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_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() }}
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 %}

View File

@ -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 %}

View File

@ -0,0 +1,68 @@
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() }}
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() }}
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() }} GB
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 %}

View File

@ -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 *
from redfish.main import connect
standard_library.install_aliases()
try:
__version__ = pbr.version.VersionInfo('redfish').release_string()

View File

@ -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,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")
@ -59,15 +58,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

View File

@ -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 <server>: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):

View File

@ -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
@ -125,7 +126,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
@ -142,8 +142,7 @@ def connect(
password,
simulator=False,
enforceSSL=True,
verify_cert=True
):
verify_cert=True):
return RedfishConnection(
url,
@ -206,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,
@ -218,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:
@ -229,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.
@ -241,28 +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
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()
@ -282,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
@ -295,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,

View File

@ -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'
return 'Members'

View File

@ -18,6 +18,8 @@ from . import mapping
from . import exception
standard_library.install_aliases()
standard_library.install_aliases()
# Global variable
@ -26,11 +28,16 @@ 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)
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(
@ -48,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.
@ -62,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)
@ -140,27 +149,134 @@ 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)
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: dict
'''
try:
return self.data.Status
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.'''
def get_api_version(self):
'''Return api version.
:returns: string -- version
:returns: api version
:rtype: string
:raises: AttributeError
'''
@ -174,44 +290,39 @@ 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)
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
@ -220,6 +331,18 @@ class Managers(Base):
# 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
@ -233,43 +356,11 @@ 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
:returns: list -- chassis ids or "Not available"
:returns: chassis ids or "Not available"
:rtype: list
'''
chassis_list = []
@ -277,7 +368,9 @@ class Managers(Base):
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:
@ -286,7 +379,8 @@ class Managers(Base):
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 = []
@ -294,7 +388,9 @@ class Managers(Base):
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:
@ -328,10 +424,11 @@ 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(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.
@ -343,6 +440,33 @@ class Systems(Base):
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
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.
@ -366,43 +490,150 @@ 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.
def get_hostname(self):
'''Get hostname of the system.
:returns: string -- serial number
:returns: hostname 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 ''
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_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_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
: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
@ -455,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):
@ -500,9 +733,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
@ -551,3 +788,180 @@ class EthernetInterfaces(Base):
except AttributeError:
return "Not available"
class ProcessorsCollection(BaseCollection):
'''Class to manage redfish ProcessorsCollection 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"
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"
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.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"
:rtype: string
'''
try:
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