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'
|
__author__ = 'HPE'
|
||||||
|
|
||||||
from six.moves.urllib import parse
|
from six.moves.urllib import parse
|
||||||
|
import sushy
|
||||||
|
|
||||||
from proliantutils import exception
|
from proliantutils import exception
|
||||||
from proliantutils.ilo import operations
|
from proliantutils.ilo import operations
|
||||||
from proliantutils import log
|
from proliantutils import log
|
||||||
from proliantutils import rest
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Class specific for Redfish APIs.
|
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__)
|
LOG = log.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -63,62 +72,69 @@ class RedfishOperations(operations.IloOperations):
|
|||||||
the root service and version. Defaults to /redfish/v1
|
the root service and version. Defaults to /redfish/v1
|
||||||
"""
|
"""
|
||||||
super(RedfishOperations, self).__init__()
|
super(RedfishOperations, self).__init__()
|
||||||
self._conn = rest.RestConnectorBase(redfish_controller_ip, username,
|
address = ('https://' + redfish_controller_ip)
|
||||||
password, bios_password, cacert)
|
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
|
self._root_prefix = root_prefix
|
||||||
# Fetch the ServiceRoot response
|
|
||||||
self._fetch_root_resources()
|
|
||||||
|
|
||||||
def _fetch_root_resources(self):
|
try:
|
||||||
"""Fetches the service root resources
|
self._sushy = sushy.Sushy(
|
||||||
|
address, username=username, password=password,
|
||||||
Retrieves/fetches the resources at ServiceRoot
|
root_prefix=root_prefix, verify=verify)
|
||||||
:raises: IloConnectionError
|
except sushy.exceptions.SushyError as e:
|
||||||
"""
|
msg = (self._('The Redfish controller at "%(controller)s" has '
|
||||||
status, headers, service_root_resp = (
|
'thrown error. Error %(error)s') %
|
||||||
self._conn._rest_get(self._root_prefix))
|
{'controller': address, 'error': str(e)})
|
||||||
self._root_resp = service_root_resp
|
LOG.debug(msg)
|
||||||
|
raise exception.IloConnectionError(msg)
|
||||||
|
|
||||||
def _get_system_collection_path(self):
|
def _get_system_collection_path(self):
|
||||||
"""Helper function to find the SystemCollection path"""
|
"""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:
|
if not systems_col:
|
||||||
raise exception.MissingAttributeError(attribute='Systems',
|
raise exception.MissingAttributeError(attribute='Systems',
|
||||||
resource=self._root_prefix)
|
resource=self._root_prefix)
|
||||||
return systems_col.get('@odata.id')
|
return systems_col.get('@odata.id')
|
||||||
|
|
||||||
def _get_system_details(self, system_id):
|
def _get_sushy_system(self, system_id):
|
||||||
"""Get the system details.
|
"""Get the sushy system for system_id
|
||||||
|
|
||||||
:param system_id: The identity of the System resource
|
:param system_id: The identity of the System resource
|
||||||
|
:returns: the Sushy system instance
|
||||||
:raises: IloError
|
:raises: IloError
|
||||||
:raises: MissingAttributeError
|
|
||||||
"""
|
"""
|
||||||
system_url = parse.urljoin(self._get_system_collection_path(),
|
system_url = parse.urljoin(self._get_system_collection_path(),
|
||||||
system_id)
|
system_id)
|
||||||
status, headers, system = self._conn._rest_get(system_url)
|
try:
|
||||||
return system
|
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):
|
def get_product_name(self):
|
||||||
"""Gets the product name of the server.
|
"""Gets the product name of the server.
|
||||||
|
|
||||||
:returns: server model name.
|
:returns: server model name.
|
||||||
:raises: IloError, on an error from iLO.
|
:raises: IloError, on an error from iLO.
|
||||||
:raises: MissingAttributeError
|
|
||||||
"""
|
"""
|
||||||
# Assuming only one system present as part of collection,
|
# Assuming only one system present as part of collection,
|
||||||
# as we are dealing with iLO's here.
|
# as we are dealing with iLO's here.
|
||||||
system = self._get_system_details('1')
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
return system['Model']
|
return sushy_system.json.get('Model')
|
||||||
|
|
||||||
def get_host_power_status(self):
|
def get_host_power_status(self):
|
||||||
"""Request the power state of the server.
|
"""Request the power state of the server.
|
||||||
|
|
||||||
:returns: Power State of the server, 'ON' or 'OFF'
|
:returns: Power State of the server, 'ON' or 'OFF'
|
||||||
:raises: IloError, on an error from iLO.
|
: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.
|
# as we are dealing with iLO's here.
|
||||||
system = self._get_system_details('1')
|
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
|
||||||
return system['PowerState'].upper()
|
return GET_POWER_STATE_MAP.get(sushy_system.power_state)
|
||||||
|
@ -16,61 +16,65 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
import sushy
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from proliantutils import exception
|
from proliantutils import exception
|
||||||
from proliantutils.redfish import redfish
|
from proliantutils.redfish import redfish
|
||||||
from proliantutils import rest
|
|
||||||
|
|
||||||
|
|
||||||
class RedfishOperationsTestCase(testtools.TestCase):
|
class RedfishOperationsTestCase(testtools.TestCase):
|
||||||
|
|
||||||
@mock.patch.object(rest, 'RestConnectorBase', autospec=True)
|
@mock.patch.object(sushy, 'Sushy', autospec=True)
|
||||||
def setUp(self, rest_connector_mock):
|
def setUp(self, sushy_mock):
|
||||||
super(RedfishOperationsTestCase, self).setUp()
|
super(RedfishOperationsTestCase, self).setUp()
|
||||||
self.conn = mock.MagicMock()
|
self.sushy = mock.MagicMock()
|
||||||
rest_connector_mock.return_value = self.conn
|
sushy_mock.return_value = self.sushy
|
||||||
with open('proliantutils/tests/redfish/'
|
with open('proliantutils/tests/redfish/'
|
||||||
'json_samples/root.json', 'r') as f:
|
'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(
|
self.rf_client = redfish.RedfishOperations(
|
||||||
'1.2.3.4', username='foo', password='bar')
|
'1.2.3.4', username='foo', password='bar')
|
||||||
rest_connector_mock.assert_called_once_with(
|
sushy_mock.assert_called_once_with(
|
||||||
'1.2.3.4', 'foo', 'bar', None, None)
|
'https://1.2.3.4', 'foo', 'bar', '/redfish/v1/', False)
|
||||||
|
|
||||||
def test__fetch_root_resources(self):
|
@mock.patch.object(sushy, 'Sushy', autospec=True)
|
||||||
rf_client = self.rf_client
|
def test_sushy_init_fail(self, sushy_mock):
|
||||||
rf_client._fetch_root_resources()
|
sushy_mock.side_effect = sushy.exceptions.SushyError
|
||||||
self.assertEqual('HPE RESTful Root Service',
|
self.assertRaisesRegex(
|
||||||
rf_client._root_resp.get('Name'))
|
exception.IloConnectionError,
|
||||||
self.assertEqual('1.0.0', rf_client._root_resp.get('RedfishVersion'))
|
'The Redfish controller at "https://1.2.3.4" has thrown error',
|
||||||
self.assertEqual('7704b47b-2fbe-5920-99a5-b766dd84cc28',
|
redfish.RedfishOperations,
|
||||||
rf_client._root_resp.get('UUID'))
|
'1.2.3.4', username='foo', password='bar')
|
||||||
for resource in ['Systems', 'Managers', 'Chassis']:
|
|
||||||
self.assertTrue(resource in rf_client._root_resp)
|
|
||||||
|
|
||||||
def test__get_system_collection_path(self):
|
def test__get_system_collection_path(self):
|
||||||
self.assertEqual('/redfish/v1/Systems/',
|
self.assertEqual('/redfish/v1/Systems/',
|
||||||
self.rf_client._get_system_collection_path())
|
self.rf_client._get_system_collection_path())
|
||||||
|
|
||||||
def test__get_system_collection_path_missing_systems_attr(self):
|
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(
|
self.assertRaisesRegex(
|
||||||
exception.MissingAttributeError,
|
exception.MissingAttributeError,
|
||||||
'The attribute Systems is missing',
|
'The attribute Systems is missing',
|
||||||
self.rf_client._get_system_collection_path)
|
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):
|
def test_get_product_name(self):
|
||||||
with open('proliantutils/tests/redfish/'
|
with open('proliantutils/tests/redfish/'
|
||||||
'json_samples/system.json', 'r') as f:
|
'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()
|
product_name = self.rf_client.get_product_name()
|
||||||
self.assertEqual('ProLiant DL180 Gen10', product_name)
|
self.assertEqual('ProLiant DL180 Gen10', product_name)
|
||||||
|
|
||||||
def test_get_host_power_status(self):
|
def test_get_host_power_status(self):
|
||||||
with open('proliantutils/tests/redfish/'
|
self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_ON
|
||||||
'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()
|
power_state = self.rf_client.get_host_power_status()
|
||||||
self.assertEqual('ON', power_state)
|
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
|
requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
|
||||||
retrying!=1.3.0,>=1.2.3 # Apache-2.0
|
retrying!=1.3.0,>=1.2.3 # Apache-2.0
|
||||||
pysnmp>=4.2.3,<5.0.0 # BSD
|
pysnmp>=4.2.3,<5.0.0 # BSD
|
||||||
|
|
||||||
|
# Redfish communication uses the Sushy library
|
||||||
|
sushy
|
||||||
|
Loading…
x
Reference in New Issue
Block a user