summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--proliantutils/redfish/redfish.py70
-rw-r--r--proliantutils/tests/redfish/test_redfish.py50
-rw-r--r--requirements.txt3
3 files changed, 73 insertions, 50 deletions
diff --git a/proliantutils/redfish/redfish.py b/proliantutils/redfish/redfish.py
index 17077a8..540b97b 100644
--- a/proliantutils/redfish/redfish.py
+++ b/proliantutils/redfish/redfish.py
@@ -15,17 +15,26 @@
15__author__ = 'HPE' 15__author__ = 'HPE'
16 16
17from six.moves.urllib import parse 17from six.moves.urllib import parse
18import sushy
18 19
19from proliantutils import exception 20from proliantutils import exception
20from proliantutils.ilo import operations 21from proliantutils.ilo import operations
21from proliantutils import log 22from proliantutils import log
22from proliantutils import rest
23 23
24 24
25""" 25"""
26Class specific for Redfish APIs. 26Class specific for Redfish APIs.
27""" 27"""
28 28
29GET_POWER_STATE_MAP = {
30 sushy.SYSTEM_POWER_STATE_ON: 'ON',
31 sushy.SYSTEM_POWER_STATE_POWERING_ON: 'ON',
32 sushy.SYSTEM_POWER_STATE_OFF: 'OFF',
33 sushy.SYSTEM_POWER_STATE_POWERING_OFF: 'OFF'
34}
35
36PROLIANT_SYSTEM_ID = '1'
37
29LOG = log.get_logger(__name__) 38LOG = log.get_logger(__name__)
30 39
31 40
@@ -63,62 +72,69 @@ class RedfishOperations(operations.IloOperations):
63 the root service and version. Defaults to /redfish/v1 72 the root service and version. Defaults to /redfish/v1
64 """ 73 """
65 super(RedfishOperations, self).__init__() 74 super(RedfishOperations, self).__init__()
66 self._conn = rest.RestConnectorBase(redfish_controller_ip, username, 75 address = ('https://' + redfish_controller_ip)
67 password, bios_password, cacert) 76 LOG.debug('Redfish address: %s', address)
68 self._root_prefix = root_prefix 77 verify = False if cacert is None else cacert
69 # Fetch the ServiceRoot response
70 self._fetch_root_resources()
71 78
72 def _fetch_root_resources(self): 79 # for error reporting purpose
73 """Fetches the service root resources 80 self.host = redfish_controller_ip
81 self._root_prefix = root_prefix
74 82
75 Retrieves/fetches the resources at ServiceRoot 83 try:
76 :raises: IloConnectionError 84 self._sushy = sushy.Sushy(
77 """ 85 address, username=username, password=password,
78 status, headers, service_root_resp = ( 86 root_prefix=root_prefix, verify=verify)
79 self._conn._rest_get(self._root_prefix)) 87 except sushy.exceptions.SushyError as e:
80 self._root_resp = service_root_resp 88 msg = (self._('The Redfish controller at "%(controller)s" has '
89 'thrown error. Error %(error)s') %
90 {'controller': address, 'error': str(e)})
91 LOG.debug(msg)
92 raise exception.IloConnectionError(msg)
81 93
82 def _get_system_collection_path(self): 94 def _get_system_collection_path(self):
83 """Helper function to find the SystemCollection path""" 95 """Helper function to find the SystemCollection path"""
84 systems_col = self._root_resp.get('Systems') 96 systems_col = self._sushy.json.get('Systems')
85 if not systems_col: 97 if not systems_col:
86 raise exception.MissingAttributeError(attribute='Systems', 98 raise exception.MissingAttributeError(attribute='Systems',
87 resource=self._root_prefix) 99 resource=self._root_prefix)
88 return systems_col.get('@odata.id') 100 return systems_col.get('@odata.id')
89 101
90 def _get_system_details(self, system_id): 102 def _get_sushy_system(self, system_id):
91 """Get the system details. 103 """Get the sushy system for system_id
92 104
93 :param system_id: The identity of the System resource 105 :param system_id: The identity of the System resource
106 :returns: the Sushy system instance
94 :raises: IloError 107 :raises: IloError
95 :raises: MissingAttributeError
96 """ 108 """
97 system_url = parse.urljoin(self._get_system_collection_path(), 109 system_url = parse.urljoin(self._get_system_collection_path(),
98 system_id) 110 system_id)
99 status, headers, system = self._conn._rest_get(system_url) 111 try:
100 return system 112 return self._sushy.get_system(system_url)
113 except sushy.exceptions.SushyError as e:
114 msg = (self._('The Redfish System "%(system)s" was not found. '
115 'Error %(error)s') %
116 {'system': system_id, 'error': str(e)})
117 LOG.debug(msg)
118 raise exception.IloError(msg)
101 119
102 def get_product_name(self): 120 def get_product_name(self):
103 """Gets the product name of the server. 121 """Gets the product name of the server.
104 122
105 :returns: server model name. 123 :returns: server model name.
106 :raises: IloError, on an error from iLO. 124 :raises: IloError, on an error from iLO.
107 :raises: MissingAttributeError
108 """ 125 """
109 # Assuming only one system present as part of collection, 126 # Assuming only one system present as part of collection,
110 # as we are dealing with iLO's here. 127 # as we are dealing with iLO's here.
111 system = self._get_system_details('1') 128 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
112 return system['Model'] 129 return sushy_system.json.get('Model')
113 130
114 def get_host_power_status(self): 131 def get_host_power_status(self):
115 """Request the power state of the server. 132 """Request the power state of the server.
116 133
117 :returns: Power State of the server, 'ON' or 'OFF' 134 :returns: Power State of the server, 'ON' or 'OFF'
118 :raises: IloError, on an error from iLO. 135 :raises: IloError, on an error from iLO.
119 :raises: MissingAttributeError
120 """ 136 """
121 # Assuming only one system present as part of collection, 137 # Assuming only one sushy_system present as part of collection,
122 # as we are dealing with iLO's here. 138 # as we are dealing with iLO's here.
123 system = self._get_system_details('1') 139 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
124 return system['PowerState'].upper() 140 return GET_POWER_STATE_MAP.get(sushy_system.power_state)
diff --git a/proliantutils/tests/redfish/test_redfish.py b/proliantutils/tests/redfish/test_redfish.py
index df26572..0df709b 100644
--- a/proliantutils/tests/redfish/test_redfish.py
+++ b/proliantutils/tests/redfish/test_redfish.py
@@ -16,61 +16,65 @@
16import json 16import json
17 17
18import mock 18import mock
19import sushy
19import testtools 20import testtools
20 21
21from proliantutils import exception 22from proliantutils import exception
22from proliantutils.redfish import redfish 23from proliantutils.redfish import redfish
23from proliantutils import rest
24 24
25 25
26class RedfishOperationsTestCase(testtools.TestCase): 26class RedfishOperationsTestCase(testtools.TestCase):
27 27
28 @mock.patch.object(rest, 'RestConnectorBase', autospec=True) 28 @mock.patch.object(sushy, 'Sushy', autospec=True)
29 def setUp(self, rest_connector_mock): 29 def setUp(self, sushy_mock):
30 super(RedfishOperationsTestCase, self).setUp() 30 super(RedfishOperationsTestCase, self).setUp()
31 self.conn = mock.MagicMock() 31 self.sushy = mock.MagicMock()
32 rest_connector_mock.return_value = self.conn 32 sushy_mock.return_value = self.sushy
33 with open('proliantutils/tests/redfish/' 33 with open('proliantutils/tests/redfish/'
34 'json_samples/root.json', 'r') as f: 34 'json_samples/root.json', 'r') as f:
35 self.conn._rest_get.return_value = 200, None, json.loads(f.read()) 35 self.sushy.json = json.loads(f.read())
36 36
37 self.rf_client = redfish.RedfishOperations( 37 self.rf_client = redfish.RedfishOperations(
38 '1.2.3.4', username='foo', password='bar') 38 '1.2.3.4', username='foo', password='bar')
39 rest_connector_mock.assert_called_once_with( 39 sushy_mock.assert_called_once_with(
40 '1.2.3.4', 'foo', 'bar', None, None) 40 'https://1.2.3.4', 'foo', 'bar', '/redfish/v1/', False)
41 41
42 def test__fetch_root_resources(self): 42 @mock.patch.object(sushy, 'Sushy', autospec=True)
43 rf_client = self.rf_client 43 def test_sushy_init_fail(self, sushy_mock):
44 rf_client._fetch_root_resources() 44 sushy_mock.side_effect = sushy.exceptions.SushyError
45 self.assertEqual('HPE RESTful Root Service', 45 self.assertRaisesRegex(
46 rf_client._root_resp.get('Name')) 46 exception.IloConnectionError,
47 self.assertEqual('1.0.0', rf_client._root_resp.get('RedfishVersion')) 47 'The Redfish controller at "https://1.2.3.4" has thrown error',
48 self.assertEqual('7704b47b-2fbe-5920-99a5-b766dd84cc28', 48 redfish.RedfishOperations,
49 rf_client._root_resp.get('UUID')) 49 '1.2.3.4', username='foo', password='bar')
50 for resource in ['Systems', 'Managers', 'Chassis']:
51 self.assertTrue(resource in rf_client._root_resp)
52 50
53 def test__get_system_collection_path(self): 51 def test__get_system_collection_path(self):
54 self.assertEqual('/redfish/v1/Systems/', 52 self.assertEqual('/redfish/v1/Systems/',
55 self.rf_client._get_system_collection_path()) 53 self.rf_client._get_system_collection_path())
56 54
57 def test__get_system_collection_path_missing_systems_attr(self): 55 def test__get_system_collection_path_missing_systems_attr(self):
58 self.rf_client._root_resp.pop('Systems') 56 self.rf_client._sushy.json.pop('Systems')
59 self.assertRaisesRegex( 57 self.assertRaisesRegex(
60 exception.MissingAttributeError, 58 exception.MissingAttributeError,
61 'The attribute Systems is missing', 59 'The attribute Systems is missing',
62 self.rf_client._get_system_collection_path) 60 self.rf_client._get_system_collection_path)
63 61
62 def test__get_sushy_system_fail(self):
63 self.rf_client._sushy.get_system.side_effect = (
64 sushy.exceptions.SushyError)
65 self.assertRaisesRegex(
66 exception.IloError,
67 'The Redfish System "apple" was not found.',
68 self.rf_client._get_sushy_system, 'apple')
69
64 def test_get_product_name(self): 70 def test_get_product_name(self):
65 with open('proliantutils/tests/redfish/' 71 with open('proliantutils/tests/redfish/'
66 'json_samples/system.json', 'r') as f: 72 'json_samples/system.json', 'r') as f:
67 self.conn._rest_get.return_value = 200, None, json.loads(f.read()) 73 self.sushy.get_system().json = json.loads(f.read())
68 product_name = self.rf_client.get_product_name() 74 product_name = self.rf_client.get_product_name()
69 self.assertEqual('ProLiant DL180 Gen10', product_name) 75 self.assertEqual('ProLiant DL180 Gen10', product_name)
70 76
71 def test_get_host_power_status(self): 77 def test_get_host_power_status(self):
72 with open('proliantutils/tests/redfish/' 78 self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_ON
73 'json_samples/system.json', 'r') as f:
74 self.conn._rest_get.return_value = 200, None, json.loads(f.read())
75 power_state = self.rf_client.get_host_power_status() 79 power_state = self.rf_client.get_host_power_status()
76 self.assertEqual('ON', power_state) 80 self.assertEqual('ON', power_state)
diff --git a/requirements.txt b/requirements.txt
index 25420ab..1ce9752 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,6 @@ jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
7requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0 7requests!=2.12.2,!=2.13.0,>=2.10.0 # Apache-2.0
8retrying!=1.3.0,>=1.2.3 # Apache-2.0 8retrying!=1.3.0,>=1.2.3 # Apache-2.0
9pysnmp>=4.2.3,<5.0.0 # BSD 9pysnmp>=4.2.3,<5.0.0 # BSD
10
11# Redfish communication uses the Sushy library
12sushy