Aligning redfish calls to new framework

This patch proposes a new framework to communicate on redfish
protocol in proliantutils.

Change-Id: I719ee8da74ba4109ac1c9dfffdf54baed67cd95c
Closes-Bug: 1646685
This commit is contained in:
Debayan Ray 2017-05-19 03:53:37 -04:00
parent 2932ddfe90
commit 100c058a09
3 changed files with 73 additions and 50 deletions

View File

@ -15,17 +15,26 @@
__author__ = 'HPE'
from six.moves.urllib import parse
import sushy
from proliantutils import exception
from proliantutils.ilo import operations
from proliantutils import log
from proliantutils import rest
"""
Class specific for Redfish APIs.
"""
GET_POWER_STATE_MAP = {
sushy.SYSTEM_POWER_STATE_ON: 'ON',
sushy.SYSTEM_POWER_STATE_POWERING_ON: 'ON',
sushy.SYSTEM_POWER_STATE_OFF: 'OFF',
sushy.SYSTEM_POWER_STATE_POWERING_OFF: 'OFF'
}
PROLIANT_SYSTEM_ID = '1'
LOG = log.get_logger(__name__)
@ -63,62 +72,69 @@ class RedfishOperations(operations.IloOperations):
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)
address = ('https://' + redfish_controller_ip)
LOG.debug('Redfish address: %s', address)
verify = False if cacert is None else cacert
# for error reporting purpose
self.host = redfish_controller_ip
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
try:
self._sushy = sushy.Sushy(
address, username=username, password=password,
root_prefix=root_prefix, verify=verify)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller at "%(controller)s" has '
'thrown error. Error %(error)s') %
{'controller': address, 'error': str(e)})
LOG.debug(msg)
raise exception.IloConnectionError(msg)
def _get_system_collection_path(self):
"""Helper function to find the SystemCollection path"""
systems_col = self._root_resp.get('Systems')
systems_col = self._sushy.json.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.
def _get_sushy_system(self, system_id):
"""Get the sushy system for system_id
:param system_id: The identity of the System resource
:returns: the Sushy system instance
: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
try:
return self._sushy.get_system(system_url)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish System "%(system)s" was not found. '
'Error %(error)s') %
{'system': system_id, 'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
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']
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
return sushy_system.json.get('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,
# Assuming only one sushy_system present as part of collection,
# as we are dealing with iLO's here.
system = self._get_system_details('1')
return system['PowerState'].upper()
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
return GET_POWER_STATE_MAP.get(sushy_system.power_state)

View File

@ -16,61 +16,65 @@
import json
import mock
import sushy
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):
@mock.patch.object(sushy, 'Sushy', autospec=True)
def setUp(self, sushy_mock):
super(RedfishOperationsTestCase, self).setUp()
self.conn = mock.MagicMock()
rest_connector_mock.return_value = self.conn
self.sushy = mock.MagicMock()
sushy_mock.return_value = self.sushy
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.sushy.json = 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)
sushy_mock.assert_called_once_with(
'https://1.2.3.4', 'foo', 'bar', '/redfish/v1/', False)
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)
@mock.patch.object(sushy, 'Sushy', autospec=True)
def test_sushy_init_fail(self, sushy_mock):
sushy_mock.side_effect = sushy.exceptions.SushyError
self.assertRaisesRegex(
exception.IloConnectionError,
'The Redfish controller at "https://1.2.3.4" has thrown error',
redfish.RedfishOperations,
'1.2.3.4', username='foo', password='bar')
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.rf_client._sushy.json.pop('Systems')
self.assertRaisesRegex(
exception.MissingAttributeError,
'The attribute Systems is missing',
self.rf_client._get_system_collection_path)
def test__get_sushy_system_fail(self):
self.rf_client._sushy.get_system.side_effect = (
sushy.exceptions.SushyError)
self.assertRaisesRegex(
exception.IloError,
'The Redfish System "apple" was not found.',
self.rf_client._get_sushy_system, 'apple')
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())
self.sushy.get_system().json = 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())
self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_ON
power_state = self.rf_client.get_host_power_status()
self.assertEqual('ON', power_state)

View File

@ -7,3 +7,6 @@ jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
retrying!=1.3.0,>=1.2.3 # Apache-2.0
pysnmp>=4.2.3,<5.0.0 # BSD
# Redfish communication uses the Sushy library
sushy