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:
parent
2932ddfe90
commit
100c058a09
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user