Initial redfish commit

This patch is again targeted towards support of consuming
Redfish APIs. This introduces the initial commit for redfish
related files. Till now, the redfish code base is **not** yet
instrumented to be called from ``ilo.client.IloClient`` object.
A separate patch will be submitted to instrument this redfish
code for Gen10. Till that this patch turns out to be a
pre-enablement of redfish on proliantutils.

Change-Id: Ib7f7596053543c3ebfa115ace3b56979ea5d6a36
Partial-Bug: 1646685
This commit is contained in:
Debayan Ray 2017-04-10 07:14:02 -04:00
parent 967a7447d8
commit 61aa195a5a
7 changed files with 533 additions and 0 deletions

View File

@ -200,3 +200,20 @@ class HpsumOperationError(ProliantUtilsException):
message = self.message % kwargs
super(HpsumOperationError, self).__init__(message)
class RedfishError(ProliantUtilsException):
"""Basic exception for errors raised by Redfish operations."""
message = None
def __init__(self, **kwargs):
if self.message and kwargs:
self.message = self.message % kwargs
super(RedfishError, self).__init__(self.message)
class MissingAttributeError(RedfishError):
message = ('The attribute %(attribute)s is missing from the '
'resource %(resource)s')

View File

View File

@ -0,0 +1,124 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# 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.
__author__ = 'HPE'
from six.moves.urllib import parse
from proliantutils import exception
from proliantutils.ilo import operations
from proliantutils import log
from proliantutils import rest
"""
Class specific for Redfish APIs.
"""
LOG = log.get_logger(__name__)
class RedfishOperations(operations.IloOperations):
"""Operations supported on redfish based hardware.
This is the list of APIs which are currently supported via Redfish mode
of operation. This is a growing list which needs to be updated as and when
the existing API/s (of its cousin RIS and RIBCL interfaces) are migrated.
--- START ---
* get_product_name(self)
* get_host_power_status(self)
--- END ---
"""
def __init__(self, redfish_controller_ip, username, password,
bios_password=None, cacert=None, root_prefix='/redfish/v1/'):
"""A class representing supported RedfishOperations
:param redfish_controller_ip: The ip address of the Redfish controller.
:param username: User account with admin/server-profile access
privilege
:param password: User account password
:param bios_password: bios password
:param cacert: a path to a CA_BUNDLE file or directory with
certificates of trusted CAs. If set to None, the driver will
ignore verifying the SSL certificate; if it's a path the driver
will use the specified certificate or one of the certificates in
the directory. Defaults to None.
:param root_prefix: The default URL prefix. This part includes
the root service and version. Defaults to /redfish/v1
"""
super(RedfishOperations, self).__init__()
self._conn = rest.RestConnectorBase(redfish_controller_ip, username,
password, bios_password, cacert)
self._root_prefix = root_prefix
# Fetch the ServiceRoot response
self._fetch_root_resources()
def _fetch_root_resources(self):
"""Fetches the service root resources
Retrieves/fetches the resources at ServiceRoot
:raises: IloConnectionError
"""
status, headers, service_root_resp = (
self._conn._rest_get(self._root_prefix))
self._root_resp = service_root_resp
def _get_system_collection_path(self):
"""Helper function to find the SystemCollection path"""
systems_col = self._root_resp.get('Systems')
if not systems_col:
raise exception.MissingAttributeError(attribute='Systems',
resource=self._root_prefix)
return systems_col.get('@odata.id')
def _get_system_details(self, system_id):
"""Get the system details.
:param system_id: The identity of the System resource
:raises: IloError
:raises: MissingAttributeError
"""
system_url = parse.urljoin(self._get_system_collection_path(),
system_id)
status, headers, system = self._conn._rest_get(system_url)
return system
def get_product_name(self):
"""Gets the product name of the server.
:returns: server model name.
:raises: IloError, on an error from iLO.
:raises: MissingAttributeError
"""
# Assuming only one system present as part of collection,
# as we are dealing with iLO's here.
system = self._get_system_details('1')
return system['Model']
def get_host_power_status(self):
"""Request the power state of the server.
:returns: Power State of the server, 'ON' or 'OFF'
:raises: IloError, on an error from iLO.
:raises: MissingAttributeError
"""
# Assuming only one system present as part of collection,
# as we are dealing with iLO's here.
system = self._get_system_details('1')
return system['PowerState'].upper()

View File

View File

@ -0,0 +1,90 @@
{
"@odata.context": "/redfish/v1/$metadata#ServiceRoot",
"@odata.etag": "W/\"06F9101D\"",
"@odata.id": "/redfish/v1/",
"@odata.type": "#ServiceRoot.v1_1_0.ServiceRoot",
"AccountService": {
"@odata.id": "/redfish/v1/AccountService/"
},
"Chassis": {
"@odata.id": "/redfish/v1/Chassis/"
},
"EventService": {
"@odata.id": "/redfish/v1/EventService/"
},
"Id": "v1",
"JsonSchemas": {
"@odata.id": "/redfish/v1/Schemas/"
},
"Links": {
"Sessions": {
"@odata.id": "/redfish/v1/SessionService/Sessions/"
}
},
"Managers": {
"@odata.id": "/redfish/v1/Managers/"
},
"Name": "HPE RESTful Root Service",
"Oem": {
"Hpe": {
"@odata.type": "#HpeiLOServiceExt.v2_0_0.HpeiLOServiceExt",
"Links": {
"ResourceDirectory": {
"@odata.id": "/redfish/v1/ResourceDirectory/"
},
"ViewClassService": {
"@odata.id": "/redfish/v1/Views/"
}
},
"Manager": [
{
"DefaultLanguage": "en",
"FQDN": "ILO.asiapacific.hpqcorp.net",
"HostName": "ILO",
"Languages": [
{
"Language": "en",
"TranslationName": "English",
"Version": "1.10"
}
],
"ManagerFirmwareVersion": "1.10",
"ManagerType": "iLO 5"
}
],
"Sessions": {
"CertCommonName": "ILO.asiapacific.hpqcorp.net",
"CertificateLoginEnabled": false,
"KerberosEnabled": false,
"LDAPAuthLicenced": true,
"LDAPEnabled": false,
"LocalLoginEnabled": true,
"LoginFailureDelay": 0,
"LoginHint": {
"Hint": "POST to /Sessions to login using the following JSON object:",
"HintPOSTData": {
"Password": "password",
"UserName": "username"
}
},
"SecurityOverride": false,
"ServerName": ""
},
"Time": "2017-03-30T12:13:38Z"
}
},
"RedfishVersion": "1.0.0",
"Registries": {
"@odata.id": "/redfish/v1/Registries/"
},
"SessionService": {
"@odata.id": "/redfish/v1/SessionService/"
},
"Systems": {
"@odata.id": "/redfish/v1/Systems/"
},
"UUID": "7704b47b-2fbe-5920-99a5-b766dd84cc28",
"UpdateService": {
"@odata.id": "/redfish/v1/UpdateService/"
}
}

View File

@ -0,0 +1,226 @@
{
"@odata.context": "/redfish/v1/$metadata#Systems/Members/$entity",
"@odata.etag": "W/\"0E79655D\"",
"@odata.id": "/redfish/v1/Systems/1/",
"@odata.type": "#ComputerSystem.v1_2_0.ComputerSystem",
"Actions": {
"#ComputerSystem.Reset": {
"ResetType@Redfish.AllowableValues": [
"On",
"ForceOff",
"ForceRestart",
"Nmi",
"PushPowerButton"
],
"target": "/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/"
}
},
"AssetTag": "",
"Bios": {
"@odata.id": "/redfish/v1/systems/1/bios/"
},
"BiosVersion": "U31 v1.00 (03/11/2017)",
"Boot": {
"BootSourceOverrideEnabled": "Disabled",
"BootSourceOverrideMode": "UEFI",
"BootSourceOverrideTarget": "None",
"BootSourceOverrideTarget@Redfish.AllowableValues": [
"None",
"Cd",
"Hdd",
"Usb",
"SDCard",
"Utilities",
"Diags",
"BiosSetup",
"Pxe",
"UefiShell",
"UefiHttp",
"UefiTarget"
],
"UefiTargetBootSourceOverride": "None",
"UefiTargetBootSourceOverride@Redfish.AllowableValues": [
"HD(1,GPT,7F14DF43-6600-420A-9950-C028836F6A5D,0x800,0x64000)/\\EFI\\centos\\shim.efi",
"UsbClass(0xFFFF,0xFFFF,0xFF,0xFF,0xFF)",
"PciRoot(0x0)/Pci(0x17,0x0)/Sata(0x3,0x0,0x0)"
]
},
"HostName": "",
"Id": "1",
"IndicatorLED": "Off",
"Links": {
"Chassis": [
{
"@odata.id": "/redfish/v1/Chassis/1/"
}
],
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/1/"
}
]
},
"LogServices": {
"@odata.id": "/redfish/v1/Systems/1/LogServices/"
},
"Manufacturer": "HPE",
"Memory": {
"@odata.id": "/redfish/v1/Systems/1/Memory/"
},
"MemorySummary": {
"Status": {
"HealthRollup": "OK"
},
"TotalSystemMemoryGiB": 8
},
"Model": "ProLiant DL180 Gen10",
"Name": "Computer System",
"Oem": {
"Hpe": {
"@odata.type": "#HpeComputerSystemExt.v2_1_0.HpeComputerSystemExt",
"Actions": {
"#HpeComputerSystemExt.PowerButton": {
"PushType@Redfish.AllowableValues": [
"Press",
"PressAndHold"
],
"target": "/redfish/v1/Systems/1/Actions/Oem/Hpe/HpeComputerSystemExt.PowerButton/"
},
"#HpeComputerSystemExt.SystemReset": {
"ResetType@Redfish.AllowableValues": [
"ColdBoot"
],
"target": "/redfish/v1/Systems/1/Actions/Oem/Hpe/HpeComputerSystemExt.SystemReset/"
}
},
"AggregateHealthStatus": {
"AgentlessManagementService": "Unavailable",
"BiosOrHardwareHealth": {
"Status": {
"Health": "OK"
}
},
"FanRedundancy": "Redundant",
"Fans": {
"Status": {
"Health": "OK"
}
},
"Memory": {
"Status": {
"Health": "OK"
}
},
"PowerSupplies": {
"PowerSuppliesMismatch": false,
"Status": {
"Health": "OK"
}
},
"Processors": {
"Status": {
"Health": "OK"
}
},
"Storage": {
"Status": {
"Health": "OK"
}
},
"Temperatures": {
"Status": {
"Health": "OK"
}
}
},
"Bios": {
"Backup": {
"Date": "03/11/2017",
"Family": "U31",
"VersionString": "U31 v1.00 (03/11/2017)"
},
"Current": {
"Date": "03/11/2017",
"Family": "U31",
"VersionString": "U31 v1.00 (03/11/2017)"
},
"UefiClass": 2
},
"DeviceDiscoveryComplete": {
"AMSDeviceDiscovery": "NoAMS",
"DeviceDiscovery": "DataIncomplete",
"SmartArrayDiscovery": "Complete"
},
"EndOfPostDelaySeconds": null,
"Links": {
"NetworkAdapters": {
"@odata.id": "/redfish/v1/Systems/1/NetworkAdapters/"
},
"PCIDevices": {
"@odata.id": "/redfish/v1/Systems/1/PCIDevices/"
},
"PCISlots": {
"@odata.id": "/redfish/v1/Systems/1/PCISlots/"
},
"SmartStorage": {
"@odata.id": "/redfish/v1/Systems/1/SmartStorage/"
},
"USBPorts": {
"@odata.id": "/redfish/v1/Systems/1/USBPorts/"
}
},
"PCAPartNumber": "",
"PCASerialNumber": "847012-001",
"PostDiscoveryCompleteTimeStamp": "2017-03-13T11:11:48Z",
"PostDiscoveryMode": null,
"PostMode": null,
"PostState": "FinishedPost",
"PowerAllocationLimit": 500,
"PowerAutoOn": "Restore",
"PowerOnDelay": "Minimum",
"PowerRegulatorMode": "Dynamic",
"PowerRegulatorModesSupported": [
"OSControl",
"Dynamic",
"Max",
"Min"
],
"SMBIOS": {
"extref": "/smbios"
},
"VirtualProfile": "Inactive"
}
},
"PowerState": "On",
"ProcessorSummary": {
"Count": 2,
"Model": "Intel(R) Genuine processor",
"Status": {
"HealthRollup": "OK"
}
},
"Processors": {
"@odata.id": "/redfish/v1/Systems/1/Processors/"
},
"SKU": " ",
"SecureBoot": {
"@odata.id": "/redfish/v1/Systems/1/SecureBoot/"
},
"SerialNumber": " ",
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Storage": {
"@odata.id": "/redfish/v1/Systems/1/Storage/"
},
"SystemType": "Physical",
"TrustedModules": [
{
"Status": {
"State": "Absent"
}
}
],
"UUID": "00000000-0000-0000-0000-000000000000"
}

View File

@ -0,0 +1,76 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import mock
import testtools
from proliantutils import exception
from proliantutils.redfish import redfish
from proliantutils import rest
class RedfishOperationsTestCase(testtools.TestCase):
@mock.patch.object(rest, 'RestConnectorBase', autospec=True)
def setUp(self, rest_connector_mock):
super(RedfishOperationsTestCase, self).setUp()
self.conn = mock.MagicMock()
rest_connector_mock.return_value = self.conn
with open('proliantutils/tests/redfish/'
'json_samples/root.json', 'r') as f:
self.conn._rest_get.return_value = 200, None, json.loads(f.read())
self.rf_client = redfish.RedfishOperations(
'1.2.3.4', username='foo', password='bar')
rest_connector_mock.assert_called_once_with(
'1.2.3.4', 'foo', 'bar', None, None)
def test__fetch_root_resources(self):
rf_client = self.rf_client
rf_client._fetch_root_resources()
self.assertEqual('HPE RESTful Root Service',
rf_client._root_resp.get('Name'))
self.assertEqual('1.0.0', rf_client._root_resp.get('RedfishVersion'))
self.assertEqual('7704b47b-2fbe-5920-99a5-b766dd84cc28',
rf_client._root_resp.get('UUID'))
for resource in ['Systems', 'Managers', 'Chassis']:
self.assertTrue(resource in rf_client._root_resp)
def test__get_system_collection_path(self):
self.assertEqual('/redfish/v1/Systems/',
self.rf_client._get_system_collection_path())
def test__get_system_collection_path_missing_systems_attr(self):
self.rf_client._root_resp.pop('Systems')
self.assertRaisesRegex(
exception.MissingAttributeError,
'The attribute Systems is missing',
self.rf_client._get_system_collection_path)
def test_get_product_name(self):
with open('proliantutils/tests/redfish/'
'json_samples/system.json', 'r') as f:
self.conn._rest_get.return_value = 200, None, json.loads(f.read())
product_name = self.rf_client.get_product_name()
self.assertEqual('ProLiant DL180 Gen10', product_name)
def test_get_host_power_status(self):
with open('proliantutils/tests/redfish/'
'json_samples/system.json', 'r') as f:
self.conn._rest_get.return_value = 200, None, json.loads(f.read())
power_state = self.rf_client.get_host_power_status()
self.assertEqual('ON', power_state)