Add oem data

- Add oem structure
    - Add oem as a redfish submodule.
    - Modify code and files to avoid circular imports :
        - types.py contains the Base, BaseCollection and Device class on
          which all standard and oem classes are inherited.
       	- standard.py will contain the redfish standard classes.
        - oem/<oem_name> will contains specific classes for <oem_name>.
          so oem/hpe will contains hpe specific classes.
  Note: the goal of python-redfish is not to deal with oem part.
  So oem will be kept as minimal as possible, however currently
  some critical hardware characteristics are only available into the oem
  part that's the reason why we are implementing it.

- Add oem class NetworkAdapter
    - This is mainly to extract mac@ with function get_mac().
- Add classes SmartStorage, ArrayControllersCollection,
  ArrayControllers, LogicalDrivesCollection, LogicalDrives.
    - Add Logical drives methods get_capacity(), get_raid.

- Improve system template and add a couple of function.
    - Add get_structured_name().
    - Add get_uefi_path().
    - However this 2 functions provide invalid content due to the firmware.

- Review and inherit from device instead of base for some components.

Change-Id: Id13e94d75f85fec7d98f1fa005c37836c244e08a
This commit is contained in:
Uggla 2016-05-28 12:29:17 +02:00
parent 77580a87d2
commit 905a36d660
7 changed files with 973 additions and 722 deletions

View File

@ -19,13 +19,13 @@ 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 %}
{%- 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 }}
@ -47,8 +47,27 @@ Ethernet Interface :
Address ipv6 : {{ ei.get_ipv6() | join(', ') }}
{%- endfor %}
{%- else %}
This system has no ethernet interface
This system has no ethernet interface as Redfish standard data
{%- endif %}
Looking for potential OEM information :
{%- if system.data.Oem.Hp %}
Supplemental information from HPE OEM part.
{%- if system.network_adapters_collection %}
{%- for networkadapter_index in system.network_adapters_collection.network_adapters_dict | sort %}
{%- set na = system.network_adapters_collection.network_adapters_dict[networkadapter_index] %}
Network adapter id {{ networkadapter_index }} :
{{ na.get_name() }}
{#- Removing these information because the FW is not providing them correctly
{{ na.get_structured_name() }}
{{ na.get_uefi_path() }}
#}
Mac address : {{ na.get_mac() | join(', ') }}
{%- endfor %}
{%- endif %}
{%- else %}
This system has no supplemental OEM information
{%- endif %}
Simple Storage :
{%- if system.simple_storage_collection %}
@ -57,12 +76,33 @@ Simple Storage :
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 %}
{%- 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
This system has no simple storage as Redfish standard data
{%- endif %}
Looking for potential OEM information :
{%- if system.data.Oem.Hp %}
Supplemental information from HPE OEM part.
{%- if system.smart_storage %}
{%- for array_controllers_index in system.smart_storage.array_controllers_collection.array_controllers_dict | sort %}
{%- set ac = system.smart_storage.array_controllers_collection.array_controllers_dict[array_controllers_index] %}
Array controller id {{ array_controllers_index }} :
{{ ac.get_name() }}
{%- for logical_drives_index in ac.logical_drives_collection.logical_drives_dict | sort %}
{%- set ld = ac.logical_drives_collection.logical_drives_dict[logical_drives_index] %}
Logical drive id {{ logical_drives_index }} :
{{ ld.get_name() }}
Status : State : {{ ld.get_status().Health }} / Health : {{ ld.get_status().Health }}
Capacity : {{ ld.get_capacity() }} MB
Raid : {{ ld.get_raid() }}
{%- endfor %}
{%- endfor %}
{%- endif %}
{%- else %}
This system has no supplemental OEM information
{%- endif %}
--------------------------------------------------------------------------------
{% endfor %}

View File

@ -128,7 +128,7 @@ import json
from urllib.parse import urlparse, urljoin, urlunparse
import requests
from . import config
from . import types
from . import standard
from . import mapping
from . import exception
standard_library.install_aliases()
@ -207,8 +207,8 @@ class RedfishConnection(object):
config.logger.debug("Root url : %s",
self.connection_parameters.rooturl)
self.Root = types.Root(self.connection_parameters.rooturl,
self.connection_parameters)
self.Root = standard.Root(self.connection_parameters.rooturl,
self.connection_parameters)
config.logger.info("API Version : %s", self.get_api_version())
mapping.redfish_version = self.get_api_version()
@ -236,21 +236,21 @@ class RedfishConnection(object):
# TODO : Add a switch to allow the both structure
# ===================================================================
# Types
self.SessionService = types.SessionService(
# standard
self.SessionService = standard.SessionService(
self.Root.get_link_url(
mapping.redfish_mapper.map_sessionservice()),
self.connection_parameters)
self.Managers = types.ManagersCollection(
self.Managers = standard.ManagersCollection(
self.Root.get_link_url("Managers"),
self.connection_parameters)
self.Systems = types.SystemsCollection(
self.Systems = standard.SystemsCollection(
self.Root.get_link_url("Systems"),
self.connection_parameters)
self.Chassis = types.ChassisCollection(
self.Chassis = standard.ChassisCollection(
self.Root.get_link_url("Chassis"), self.connection_parameters)
# self.EventService

13
redfish/oem/__init__.py Normal file
View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

155
redfish/oem/hpe.py Normal file
View File

@ -0,0 +1,155 @@
# coding=utf-8
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
import re
from ..types import Base, BaseCollection, Device
standard_library.install_aliases()
# Global variable
class NetworkAdaptersCollection(BaseCollection):
'''Class to manage redfish hpe oem NetworkAdaptersCollection data.'''
def __init__(self, url, connection_parameters):
super(NetworkAdaptersCollection, self).__init__(url,
connection_parameters)
self.network_adapters_dict = {}
for link in self.links:
index = re.search(r'NetworkAdapters/(\w+)', link)
self.network_adapters_dict[index.group(1)] = NetworkAdapters(
link, connection_parameters)
class NetworkAdapters(Device):
'''Class to manage redfish hpe oem NetworkAdapters data.'''
def get_mac(self):
'''Get NetworkAdapters mac address
:returns: mac adresses or "Not available"
:rtype: list
'''
macaddresses = []
try:
for port in self.data.PhysicalPorts:
mac = port['MacAddress']
macaddresses.append(mac)
return macaddresses
except AttributeError:
return "Not available"
def get_structured_name(self):
'''Get NetworkAdapters StructuredName
:returns: StructuredName or "Not available"
:rtype: string
'''
try:
return self.data.StructuredName
except AttributeError:
return "Not available"
def get_uefi_path(self):
'''Get networkadapters uefi path
:returns: UEFIDevicePath or "Not available"
:rtype: string
'''
try:
return self.data.UEFIDevicePath
except AttributeError:
return "Not available"
class SmartStorage(Base):
'''Class to manage redfish hpe oem SmartStorage data.'''
def __init__(self, url, connection_parameters):
super(SmartStorage, self).__init__(url, connection_parameters)
try:
self.array_controllers_collection = \
ArrayControllersCollection(
self.get_link_url('ArrayControllers', self.data.Links),
connection_parameters)
except AttributeError:
# This means we don't have ArrayControllers
self.array_controllers_collection = None
class ArrayControllersCollection(BaseCollection):
'''Class to manage redfish hpe oem ArrayControllersCollection data.'''
def __init__(self, url, connection_parameters):
super(ArrayControllersCollection, self).__init__(url,
connection_parameters)
self.array_controllers_dict = {}
for link in self.links:
index = re.search(r'ArrayControllers/(\w+)', link)
self.array_controllers_dict[index.group(1)] = ArrayControllers(
link, connection_parameters)
class ArrayControllers(Device):
'''Class to manage redfish hpe oem ArrayControllers data.'''
def __init__(self, url, connection_parameters):
super(ArrayControllers, self).__init__(url, connection_parameters)
try:
self.logical_drives_collection = \
LogicalDrivesCollection(
self.get_link_url('LogicalDrives', self.data.Links),
connection_parameters)
except AttributeError:
# This means we don't have ArrayControllers
self.logical_drives_collection = None
class LogicalDrivesCollection(BaseCollection):
'''Class to manage redfish hpe oem LogicalDrivesCollection data.'''
def __init__(self, url, connection_parameters):
super(LogicalDrivesCollection, self).__init__(url,
connection_parameters)
self.logical_drives_dict = {}
for link in self.links:
index = re.search(r'LogicalDrives/(\w+)', link)
self.logical_drives_dict[index.group(1)] = LogicalDrives(
link, connection_parameters)
class LogicalDrives(Device):
'''Class to manage redfish hpe oem LogicalDrives data.'''
def get_capacity(self):
'''Get Logical drive capacity
:returns: Logical drive capacity or "Not available"
:rtype: string
'''
try:
return self.data.CapacityMiB
except AttributeError:
return "Not available"
def get_raid(self):
'''Get Logical drive raid configuration
:returns: Logical drive raid configuration or "Not available"
:rtype: string
'''
try:
return self.data.Raid
except AttributeError:
return "Not available"

736
redfish/standard.py Normal file
View File

@ -0,0 +1,736 @@
# coding=utf-8
from __future__ import unicode_literals
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import
from future import standard_library
import re
from urllib.parse import urljoin
import requests
from .types import Base, BaseCollection, Device
from . import mapping
from . import exception
from .oem import hpe
standard_library.install_aliases()
class Root(Base):
'''Class to manage redfish Root data.'''
def get_api_version(self):
'''Return api version.
:returns: api version
:rtype: string
:raises: AttributeError
'''
try:
version = self.data.RedfishVersion
except AttributeError:
version = self.data.ServiceVersion
version = version.replace('.', '')
version = version[0] + '.' + version[1:]
return(version)
def get_api_UUID(self):
'''Return api UUID.
:returns: api UUID
:rtype: string
'''
return self.data.UUID
class SessionService(Base):
'''Class to manage redfish SessionService data.'''
pass
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 ?
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)
except exception.InvalidRedfishContentException:
# This is to avoid invalid content from the mockup
self.ethernet_interfaces_collection = None
except AttributeError:
# 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
:returns: string -- bios version or "Not available"
'''
try:
return self.data.FirmwareVersion
except AttributeError:
# We are here because the attribute could be not defined.
# This is the case with the mockup for manager 2 and 3
return "Not available"
def get_managed_chassis(self):
'''Get managed chassis ids by the manager
: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.ManagerForChassis:
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_managed_systems(self):
'''Get managed systems ids by the manager
:returns: systems ids or "Not available"
:rtype: list
'''
systems_list = []
links = getattr(self.data, mapping.redfish_mapper.map_links(self.data))
try:
for systems in links.ManagerForServers:
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:
return "Not available"
def reset(self):
'''Reset the manager.
:returns: string -- http response of POST request
'''
# Craft the request
link = getattr(self.data.Actions, "#Manager.Reset")
link = link.target
reset_url = urljoin(self.url, link)
response = requests.post(
reset_url,
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers)
# TODO : treat response.
return response
class ManagersCollection(BaseCollection):
'''Class to manage redfish ManagersCollection data.'''
def __init__(self, url, connection_parameters):
'''Class constructor'''
super(ManagersCollection, self).__init__(url, connection_parameters)
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)
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.
def __init__(self, url, connection_parameters):
'''Class constructor'''
super(Systems, self).__init__(url, connection_parameters)
try:
self.bios = Bios(url + 'Bios/Settings', connection_parameters)
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
try:
self.data.Oem.Hp
try:
self.network_adapters_collection = \
hpe.NetworkAdaptersCollection(
self.get_link_url('NetworkAdapters',
self.data.Oem.Hp.Links),
connection_parameters)
except AttributeError:
# This means we don't have NetworkAdapters
self.network_adapters_collection = None
try:
self.smart_storage = \
hpe.SmartStorage(
self.get_link_url('SmartStorage',
self.data.Oem.Hp.Links),
connection_parameters)
except AttributeError:
# This means we don't have SmartStorage
self.smart_storage = None
except AttributeError:
# This means we don't have oem data
pass
def reset_system(self):
'''Force reset of the system.
:returns: string -- http response of POST request
'''
# Craft the request
action = dict()
action['Action'] = 'Reset'
action['ResetType'] = 'ForceRestart'
# Debug the url and perform the POST action
# print self.api_url
response = self.api_url.post(
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers,
data=action)
# TODO : treat response.
return response
def get_bios_version(self):
'''Get bios version of the system.
:returns: bios version or "Not available"
:rtype: string
'''
try:
return self.data.BiosVersion
except AttributeError:
return "Not available"
def get_hostname(self):
'''Get hostname of the system.
:returns: hostname or "Not available"
:rtype: string
'''
try:
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: system power state or "Not available"
:rtype: string
'''
try:
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
:param value: json structure with value to update
:returns: string -- http response of PATCH request
'''
# perform the POST action
# print self.api_url.url()
response = requests.patch(
self.api_url.url(),
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers,
data=value)
return response.reason
def set_boot_source_override(self, target, enabled):
'''Shotcut function to set boot source
:param target: new boot source. Supported values:
"None",
"Pxe",
"Floppy",
"Cd",
"Usb",
"Hdd",
"BiosSetup",
"Utilities",
"Diags",
"UefiShell",
"UefiTarget"
:param enabled: Supported values:
"Disabled",
"Once",
"Continuous"
:returns: string -- http response of PATCH request
'''
return self.set_parameter_json(
'{"Boot": {"BootSourceOverrideTarget": "' +
target + '"},{"BootSourceOverrideEnabled" : "' + enabled + '"}}')
class SystemsCollection(BaseCollection):
'''Class to manage redfish SystemsCollection data.'''
def __init__(self, url, connection_parameters):
super(SystemsCollection, self).__init__(url, connection_parameters)
self.systems_dict = {}
for link in self.links:
index = re.search(r'Systems/(\w+)', link)
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)
class Boot(Base):
'''Class to manage redfish Boot data.'''
def __init__(self, url, connection_parameters):
super(Boot, self).__init__(url, connection_parameters)
class EthernetInterfacesCollection(BaseCollection):
'''Class to manage redfish EthernetInterfacesColkection data.'''
def __init__(self, url, connection_parameters):
super(EthernetInterfacesCollection,
self).__init__(url, connection_parameters)
self.ethernet_interfaces_dict = {}
# Url returned by the mock up is wrong
# /redfish/v1/Managers/EthernetInterfaces/1 returns a 404.
# --> this is not true anymore (2016/01/03)
# The correct one should be /redfish/v1/Managers/1/EthernetInterfaces/1
# --> correct by mockup return invalid content (not json)
# Check more than 1 hour for this bug.... grrr....
for link in self.links:
index = re.search(r'EthernetInterfaces/(\w+)', link)
self.ethernet_interfaces_dict[index.group(1)] = \
EthernetInterfaces(link, connection_parameters)
class EthernetInterfaces(Base):
'''Class to manage redfish EthernetInterfaces.'''
def get_mac(self):
'''Get EthernetInterface MacAddress
:returns: string -- interface macaddress or "Not available"
'''
try:
# Proliant firmware seems to not follow redfish systax
return self.data.MacAddress
except AttributeError:
try:
return self.data.MACAddress
except AttributeError:
return "Not available"
def get_fqdn(self):
'''Get EthernetInterface fqdn
:returns: string -- interface fqdn or "Not available"
'''
try:
return self.data.FQDN
except AttributeError:
return "Not available"
def get_ipv4(self):
'''Get EthernetInterface ipv4 address
:returns: list -- interface ip addresses or "Not available"
'''
ipaddresses = []
try:
for ip_settings in self.data.IPv4Addresses:
address = ip_settings['Address']
ipaddresses.append(address)
return ipaddresses
except AttributeError:
return "Not available"
def get_ipv6(self):
'''Get EthernetInterface ipv6 address
:returns: list -- interface ip addresses or "Not available"
'''
ipaddresses = []
try:
for ip_settings in self.data.IPv6Addresses:
address = ip_settings['Address']
ipaddresses.append(address)
return ipaddresses
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

View File

@ -7,7 +7,6 @@ from future import standard_library
from builtins import object
import pprint
import re
from urllib.parse import urljoin
import requests
import simplejson
@ -18,7 +17,6 @@ from . import mapping
from . import exception
standard_library.install_aliases()
standard_library.install_aliases()
# Global variable
@ -57,24 +55,29 @@ class Base(object):
raise exception.InvalidRedfishContentException(msg)
config.logger.debug(pprint.PrettyPrinter(indent=4).pformat(self.data))
def get_link_url(self, link_type):
def get_link_url(self, link_type, data_subset=None):
'''Need to be explained.
:param parameter_name: name of the parameter
:returns: string -- parameter value
'''
if not data_subset:
data = self.data
else:
data = data_subset
self.links = []
# Manage standard < 1.0
if float(mapping.redfish_version) < 1.00:
links = getattr(self.data, mapping.redfish_mapper.map_links())
links = getattr(data, mapping.redfish_mapper.map_links())
if link_type in links:
return urljoin(
self.url,
links[link_type][mapping.redfish_mapper.map_links_ref()])
raise AttributeError
else:
links = getattr(self.data, link_type)
links = getattr(data, link_type)
link = getattr(links, mapping.redfish_mapper.map_links_ref())
return urljoin(self.url, link)
@ -268,700 +271,3 @@ class Device(Base):
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: api version
:rtype: string
:raises: AttributeError
'''
try:
version = self.data.RedfishVersion
except AttributeError:
version = self.data.ServiceVersion
version = version.replace('.', '')
version = version[0] + '.' + version[1:]
return(version)
def get_api_UUID(self):
'''Return api UUID.
:returns: api UUID
:rtype: string
'''
return self.data.UUID
class SessionService(Base):
'''Class to manage redfish SessionService data.'''
pass
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 ?
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)
except exception.InvalidRedfishContentException:
# This is to avoid invalid content from the mockup
self.ethernet_interfaces_collection = None
except AttributeError:
# 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
:returns: string -- bios version or "Not available"
'''
try:
return self.data.FirmwareVersion
except AttributeError:
# We are here because the attribute could be not defined.
# This is the case with the mockup for manager 2 and 3
return "Not available"
def get_managed_chassis(self):
'''Get managed chassis ids by the manager
: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.ManagerForChassis:
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_managed_systems(self):
'''Get managed systems ids by the manager
:returns: systems ids or "Not available"
:rtype: list
'''
systems_list = []
links = getattr(self.data, mapping.redfish_mapper.map_links(self.data))
try:
for systems in links.ManagerForServers:
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:
return "Not available"
def reset(self):
'''Reset the manager.
:returns: string -- http response of POST request
'''
# Craft the request
link = getattr(self.data.Actions, "#Manager.Reset")
link = link.target
reset_url = urljoin(self.url, link)
response = requests.post(
reset_url,
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers)
# TODO : treat response.
return response
class ManagersCollection(BaseCollection):
'''Class to manage redfish ManagersCollection data.'''
def __init__(self, url, connection_parameters):
'''Class constructor'''
super(ManagersCollection, self).__init__(url, connection_parameters)
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)
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.
def __init__(self, url, connection_parameters):
'''Class constructor'''
super(Systems, self).__init__(url, connection_parameters)
try:
self.bios = Bios(url + 'Bios/Settings', connection_parameters)
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.
:returns: string -- http response of POST request
'''
# Craft the request
action = dict()
action['Action'] = 'Reset'
action['ResetType'] = 'ForceRestart'
# Debug the url and perform the POST action
# print self.api_url
response = self.api_url.post(
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers,
data=action)
# TODO : treat response.
return response
def get_bios_version(self):
'''Get bios version of the system.
:returns: bios version or "Not available"
:rtype: string
'''
try:
return self.data.BiosVersion
except AttributeError:
return "Not available"
def get_hostname(self):
'''Get hostname of the system.
:returns: hostname or "Not available"
:rtype: string
'''
try:
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: system power state or "Not available"
:rtype: string
'''
try:
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
:param value: json structure with value to update
:returns: string -- http response of PATCH request
'''
# perform the POST action
# print self.api_url.url()
response = requests.patch(
self.api_url.url(),
verify=self.connection_parameters.verify_cert,
headers=self.connection_parameters.headers,
data=value)
return response.reason
def set_boot_source_override(self, target, enabled):
'''Shotcut function to set boot source
:param target: new boot source. Supported values:
"None",
"Pxe",
"Floppy",
"Cd",
"Usb",
"Hdd",
"BiosSetup",
"Utilities",
"Diags",
"UefiShell",
"UefiTarget"
:param enabled: Supported values:
"Disabled",
"Once",
"Continuous"
:returns: string -- http response of PATCH request
'''
return self.set_parameter_json(
'{"Boot": {"BootSourceOverrideTarget": "' +
target + '", "BootSourceOverrideEnabled" : "' + enabled + '"}}')
class SystemsCollection(BaseCollection):
'''Class to manage redfish SystemsCollection data.'''
def __init__(self, url, connection_parameters):
super(SystemsCollection, self).__init__(url, connection_parameters)
self.systems_dict = {}
for link in self.links:
index = re.search(r'Systems/(\w+)', link)
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)
class Boot(Base):
'''Class to manage redfish Boot data.'''
def __init__(self, url, connection_parameters):
super(Boot, self).__init__(url, connection_parameters)
class EthernetInterfacesCollection(BaseCollection):
'''Class to manage redfish EthernetInterfacesColkection data.'''
def __init__(self, url, connection_parameters):
super(EthernetInterfacesCollection,
self).__init__(url, connection_parameters)
self.ethernet_interfaces_dict = {}
# Url returned by the mock up is wrong
# /redfish/v1/Managers/EthernetInterfaces/1 returns a 404.
# --> this is not true anymore (2016/01/03)
# The correct one should be /redfish/v1/Managers/1/EthernetInterfaces/1
# --> correct by mockup return invalid content (not json)
# Check more than 1 hour for this bug.... grrr....
for link in self.links:
index = re.search(r'EthernetInterfaces/(\w+)', link)
self.ethernet_interfaces_dict[index.group(1)] = \
EthernetInterfaces(link, connection_parameters)
class EthernetInterfaces(Base):
'''Class to manage redfish EthernetInterfaces.'''
def get_mac(self):
'''Get EthernetInterface MacAddress
:returns: string -- interface macaddress or "Not available"
'''
try:
# Proliant firmware seems to not follow redfish systax
return self.data.MacAddress
except AttributeError:
try:
return self.data.MACAddress
except AttributeError:
return "Not available"
def get_fqdn(self):
'''Get EthernetInterface fqdn
:returns: string -- interface fqdn or "Not available"
'''
try:
return self.data.FQDN
except AttributeError:
return "Not available"
def get_ipv4(self):
'''Get EthernetInterface ipv4 address
:returns: list -- interface ip addresses or "Not available"
'''
ipaddresses = []
try:
for ip_settings in self.data.IPv4Addresses:
address = ip_settings['Address']
ipaddresses.append(address)
return ipaddresses
except AttributeError:
return "Not available"
def get_ipv6(self):
'''Get EthernetInterface ipv6 address
:returns: list -- interface ip addresses or "Not available"
'''
ipaddresses = []
try:
for ip_settings in self.data.IPv6Addresses:
address = ip_settings['Address']
ipaddresses.append(address)
return ipaddresses
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

View File

@ -23,6 +23,7 @@ classifier =
[files]
packages =
redfish
redfish/oem
scripts =
redfish-client/redfish-client