summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDebayan Ray <debayan.ray@gmail.com>2017-06-09 04:40:48 -0400
committerDebayan Ray <debayan.ray@gmail.com>2017-06-09 07:28:07 -0400
commite2f1bb4167d7a72f4ff5c16f405a074163f85ccf (patch)
tree0cd1eab69fa3a351b8a6e87f9b7429db6686b2c2
parent840f7d72f7ffbefabfa1414467b1eae798782f16 (diff)
Redfish: Add system push power button operations
This patch adds system power operations in proliantutils via redfish way. These 2 methods are introduced on redfish ops: 1) press_pwr_btn 2) hold_pwr_btn Change-Id: Ie6281b9ad608e50fc274a8e1b32d539b6a174e5d Partial-Bug: 1691955
Notes
Notes (review): Code-Review+2: Shivanand Tendulker <stendulker@gmail.com> Code-Review+2: Nisha Agarwal <agarwalnisha1980@gmail.com> Workflow+1: Nisha Agarwal <agarwalnisha1980@gmail.com> Verified+2: Jenkins Submitted-by: Jenkins Submitted-at: Thu, 15 Jun 2017 07:30:15 +0000 Reviewed-on: https://review.openstack.org/472631 Project: openstack/proliantutils Branch: refs/heads/master
-rw-r--r--proliantutils/ilo/client.py2
-rw-r--r--proliantutils/redfish/redfish.py55
-rw-r--r--proliantutils/redfish/resources/system/constants.py20
-rw-r--r--proliantutils/redfish/resources/system/mappings.py27
-rw-r--r--proliantutils/redfish/resources/system/system.py53
-rw-r--r--proliantutils/tests/redfish/resources/__init__.py0
-rw-r--r--proliantutils/tests/redfish/resources/test_system.py63
-rw-r--r--proliantutils/tests/redfish/test_redfish.py27
8 files changed, 229 insertions, 18 deletions
diff --git a/proliantutils/ilo/client.py b/proliantutils/ilo/client.py
index 06ad50c..2711d68 100644
--- a/proliantutils/ilo/client.py
+++ b/proliantutils/ilo/client.py
@@ -62,6 +62,8 @@ SUPPORTED_REDFISH_METHODS = [
62 'get_host_power_status', 62 'get_host_power_status',
63 'set_host_power', 63 'set_host_power',
64 'reset_server', 64 'reset_server',
65 'press_pwr_btn',
66 'hold_pwr_btn',
65] 67]
66 68
67LOG = log.get_logger(__name__) 69LOG = log.get_logger(__name__)
diff --git a/proliantutils/redfish/redfish.py b/proliantutils/redfish/redfish.py
index 90a05e1..fd965f2 100644
--- a/proliantutils/redfish/redfish.py
+++ b/proliantutils/redfish/redfish.py
@@ -21,7 +21,7 @@ from proliantutils import exception
21from proliantutils.ilo import operations 21from proliantutils.ilo import operations
22from proliantutils import log 22from proliantutils import log
23from proliantutils.redfish import main 23from proliantutils.redfish import main
24 24from proliantutils.redfish.resources.system import constants as sys_cons
25 25
26""" 26"""
27Class specific for Redfish APIs. 27Class specific for Redfish APIs.
@@ -39,6 +39,8 @@ POWER_RESET_MAP = {
39 'OFF': sushy.RESET_FORCE_OFF, 39 'OFF': sushy.RESET_FORCE_OFF,
40} 40}
41 41
42# Assuming only one sushy_system present as part of collection,
43# as we are dealing with iLO's here.
42PROLIANT_SYSTEM_ID = '1' 44PROLIANT_SYSTEM_ID = '1'
43 45
44LOG = log.get_logger(__name__) 46LOG = log.get_logger(__name__)
@@ -47,17 +49,11 @@ LOG = log.get_logger(__name__)
47class RedfishOperations(operations.IloOperations): 49class RedfishOperations(operations.IloOperations):
48 """Operations supported on redfish based hardware. 50 """Operations supported on redfish based hardware.
49 51
50 This is the list of APIs which are currently supported via Redfish mode 52 This class holds APIs which are currently supported via Redfish mode
51 of operation. This is a growing list which needs to be updated as and when 53 of operation. This is a growing list which needs to be updated as and when
52 the existing API/s (of its cousin RIS and RIBCL interfaces) are migrated. 54 the existing API/s (of its cousin RIS and RIBCL interfaces) are migrated.
53 55 For operations currently supported on the client object, please refer:
54 --- START --- 56 *proliantutils.ilo.client.SUPPORTED_REDFISH_METHODS*
55
56 * get_product_name(self)
57 * get_host_power_status(self)
58
59 --- END ---
60
61 """ 57 """
62 58
63 def __init__(self, redfish_controller_ip, username, password, 59 def __init__(self, redfish_controller_ip, username, password,
@@ -129,8 +125,6 @@ class RedfishOperations(operations.IloOperations):
129 :returns: server model name. 125 :returns: server model name.
130 :raises: IloError, on an error from iLO. 126 :raises: IloError, on an error from iLO.
131 """ 127 """
132 # Assuming only one system present as part of collection,
133 # as we are dealing with iLO's here.
134 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) 128 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
135 return sushy_system.json.get('Model') 129 return sushy_system.json.get('Model')
136 130
@@ -140,8 +134,6 @@ class RedfishOperations(operations.IloOperations):
140 :returns: Power State of the server, 'ON' or 'OFF' 134 :returns: Power State of the server, 'ON' or 'OFF'
141 :raises: IloError, on an error from iLO. 135 :raises: IloError, on an error from iLO.
142 """ 136 """
143 # Assuming only one sushy_system present as part of collection,
144 # as we are dealing with iLO's here.
145 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) 137 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
146 return GET_POWER_STATE_MAP.get(sushy_system.power_state) 138 return GET_POWER_STATE_MAP.get(sushy_system.power_state)
147 139
@@ -150,8 +142,6 @@ class RedfishOperations(operations.IloOperations):
150 142
151 :raises: IloError, on an error from iLO. 143 :raises: IloError, on an error from iLO.
152 """ 144 """
153 # Assuming only one sushy_system present as part of collection,
154 # as we are dealing with iLO's here.
155 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) 145 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
156 try: 146 try:
157 sushy_system.reset_system(sushy.RESET_FORCE_RESTART) 147 sushy_system.reset_system(sushy.RESET_FORCE_RESTART)
@@ -185,8 +175,6 @@ class RedfishOperations(operations.IloOperations):
185 "state."), {'target_value': target_value}) 175 "state."), {'target_value': target_value})
186 return 176 return
187 177
188 # Assuming only one system present as part of collection,
189 # as we are dealing with iLO's here.
190 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) 178 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
191 try: 179 try:
192 sushy_system.reset_system(POWER_RESET_MAP[target_value]) 180 sushy_system.reset_system(POWER_RESET_MAP[target_value])
@@ -196,3 +184,34 @@ class RedfishOperations(operations.IloOperations):
196 {'target_value': target_value, 'error': str(e)}) 184 {'target_value': target_value, 'error': str(e)})
197 LOG.debug(msg) 185 LOG.debug(msg)
198 raise exception.IloError(msg) 186 raise exception.IloError(msg)
187
188 def press_pwr_btn(self):
189 """Simulates a physical press of the server power button.
190
191 :raises: IloError, on an error from iLO.
192 """
193 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
194 try:
195 sushy_system.push_power_button(sys_cons.PUSH_POWER_BUTTON_PRESS)
196 except sushy.exceptions.SushyError as e:
197 msg = (self._('The Redfish controller failed to press power button'
198 ' of server. Error %(error)s') %
199 {'error': str(e)})
200 LOG.debug(msg)
201 raise exception.IloError(msg)
202
203 def hold_pwr_btn(self):
204 """Simulate a physical press and hold of the server power button.
205
206 :raises: IloError, on an error from iLO.
207 """
208 sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
209 try:
210 sushy_system.push_power_button(
211 sys_cons.PUSH_POWER_BUTTON_PRESS_AND_HOLD)
212 except sushy.exceptions.SushyError as e:
213 msg = (self._('The Redfish controller failed to press and hold '
214 'power button of server. Error %(error)s') %
215 {'error': str(e)})
216 LOG.debug(msg)
217 raise exception.IloError(msg)
diff --git a/proliantutils/redfish/resources/system/constants.py b/proliantutils/redfish/resources/system/constants.py
new file mode 100644
index 0000000..c1e9c6e
--- /dev/null
+++ b/proliantutils/redfish/resources/system/constants.py
@@ -0,0 +1,20 @@
1# Copyright 2017 Hewlett Packard Enterprise Development LP
2#
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17# Push power button action constants
18
19PUSH_POWER_BUTTON_PRESS = 'press'
20PUSH_POWER_BUTTON_PRESS_AND_HOLD = 'press and hold'
diff --git a/proliantutils/redfish/resources/system/mappings.py b/proliantutils/redfish/resources/system/mappings.py
new file mode 100644
index 0000000..9563019
--- /dev/null
+++ b/proliantutils/redfish/resources/system/mappings.py
@@ -0,0 +1,27 @@
1# Copyright 2017 Hewlett Packard Enterprise Development LP
2#
3# All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17from sushy import utils
18
19from proliantutils.redfish.resources.system import constants
20
21PUSH_POWER_BUTTON_VALUE_MAP = {
22 'Press': constants.PUSH_POWER_BUTTON_PRESS,
23 'PressAndHold': constants.PUSH_POWER_BUTTON_PRESS_AND_HOLD,
24}
25
26PUSH_POWER_BUTTON_VALUE_MAP_REV = (
27 utils.revert_dictionary(PUSH_POWER_BUTTON_VALUE_MAP))
diff --git a/proliantutils/redfish/resources/system/system.py b/proliantutils/redfish/resources/system/system.py
index c477dd1..dedde84 100644
--- a/proliantutils/redfish/resources/system/system.py
+++ b/proliantutils/redfish/resources/system/system.py
@@ -14,8 +14,27 @@
14 14
15__author__ = 'HPE' 15__author__ = 'HPE'
16 16
17from sushy.resources import base
17from sushy.resources.system import system 18from sushy.resources.system import system
18 19
20from proliantutils import exception
21from proliantutils import log
22from proliantutils.redfish.resources.system import mappings
23
24LOG = log.get_logger(__name__)
25
26
27class PowerButtonActionField(base.CompositeField):
28 allowed_values = base.Field('PushType@Redfish.AllowableValues',
29 adapter=list)
30
31 target_uri = base.Field('target', required=True)
32
33
34class HpeActionsField(base.CompositeField):
35 computer_system_ext_powerbutton = (
36 PowerButtonActionField('#HpeComputerSystemExt.PowerButton'))
37
19 38
20class HPESystem(system.System): 39class HPESystem(system.System):
21 """Class that extends the functionality of System resource class 40 """Class that extends the functionality of System resource class
@@ -23,3 +42,37 @@ class HPESystem(system.System):
23 This class extends the functionality of System resource class 42 This class extends the functionality of System resource class
24 from sushy 43 from sushy
25 """ 44 """
45
46 _hpe_actions = HpeActionsField(['Oem', 'Hpe', 'Actions'], required=True)
47 """Oem specific system extensibility actions"""
48
49 def _get_hpe_push_power_button_action_element(self):
50 push_action = self._hpe_actions.computer_system_ext_powerbutton
51 if not push_action:
52 raise exception.MissingAttributeError(
53 attribute='Oem/Hpe/Actions/#HpeComputerSystemExt.PowerButton',
54 resource=self.path)
55
56 return push_action
57
58 def push_power_button(self, target_value):
59 """Reset the system in hpe exclusive manner.
60
61 :param target_value: The target value to be set.
62 :raises: InvalidInputError, if the target value is not
63 allowed.
64 :raises: SushyError, on an error from iLO.
65 """
66 if target_value not in mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV:
67 msg = ('The parameter "%(parameter)s" value "%(target_value)s" is '
68 'invalid. Valid values are: %(valid_power_values)s' %
69 {'parameter': 'target_value', 'target_value': target_value,
70 'valid_power_values': (
71 mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV.keys())})
72 raise exception.InvalidInputError(msg)
73
74 value = mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV[target_value]
75 target_uri = (
76 self._get_hpe_push_power_button_action_element().target_uri)
77
78 self._conn.post(target_uri, data={'PushType': value})
diff --git a/proliantutils/tests/redfish/resources/__init__.py b/proliantutils/tests/redfish/resources/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/proliantutils/tests/redfish/resources/__init__.py
diff --git a/proliantutils/tests/redfish/resources/test_system.py b/proliantutils/tests/redfish/resources/test_system.py
new file mode 100644
index 0000000..2bb518d
--- /dev/null
+++ b/proliantutils/tests/redfish/resources/test_system.py
@@ -0,0 +1,63 @@
1# Copyright 2017 Hewlett Packard Enterprise Development LP
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import json
17
18import mock
19import testtools
20
21from proliantutils import exception
22from proliantutils.redfish.resources.system import constants as sys_cons
23from proliantutils.redfish.resources.system import system
24
25
26class HPESystemTestCase(testtools.TestCase):
27
28 def setUp(self):
29 super(HPESystemTestCase, self).setUp()
30 self.conn = mock.MagicMock()
31 with open('proliantutils/tests/redfish/'
32 'json_samples/system.json', 'r') as f:
33 self.conn.get.return_value.json.return_value = json.loads(f.read())
34
35 self.sys_inst = system.HPESystem(
36 self.conn, '/redfish/v1/Systems/1',
37 redfish_version='1.0.2')
38
39 def test__get_hpe_push_power_button_action_element(self):
40 value = self.sys_inst._get_hpe_push_power_button_action_element()
41 self.assertEqual("/redfish/v1/Systems/1/Actions/Oem/Hpe/"
42 "HpeComputerSystemExt.PowerButton/",
43 value.target_uri)
44 self.assertEqual(["Press", "PressAndHold"], value.allowed_values)
45
46 def test__get_hpe_push_power_button_action_element_missing_action(self):
47 self.sys_inst._hpe_actions.computer_system_ext_powerbutton = None
48 self.assertRaisesRegex(
49 exception.MissingAttributeError,
50 'Oem/Hpe/Actions/#HpeComputerSystemExt.PowerButton is missing',
51 self.sys_inst._get_hpe_push_power_button_action_element)
52
53 def test_push_power_button(self):
54 self.sys_inst.push_power_button(
55 sys_cons.PUSH_POWER_BUTTON_PRESS)
56 self.sys_inst._conn.post.assert_called_once_with(
57 '/redfish/v1/Systems/1/Actions/Oem/Hpe/'
58 'HpeComputerSystemExt.PowerButton/',
59 data={'PushType': 'Press'})
60
61 def test_push_power_button_invalid_value(self):
62 self.assertRaises(exception.InvalidInputError,
63 self.sys_inst.push_power_button, 'invalid-value')
diff --git a/proliantutils/tests/redfish/test_redfish.py b/proliantutils/tests/redfish/test_redfish.py
index 7fb5884..dbf3b1b 100644
--- a/proliantutils/tests/redfish/test_redfish.py
+++ b/proliantutils/tests/redfish/test_redfish.py
@@ -22,6 +22,7 @@ import testtools
22from proliantutils import exception 22from proliantutils import exception
23from proliantutils.redfish import main 23from proliantutils.redfish import main
24from proliantutils.redfish import redfish 24from proliantutils.redfish import redfish
25from proliantutils.redfish.resources.system import constants as sys_cons
25 26
26 27
27class RedfishOperationsTestCase(testtools.TestCase): 28class RedfishOperationsTestCase(testtools.TestCase):
@@ -122,3 +123,29 @@ class RedfishOperationsTestCase(testtools.TestCase):
122 self.rf_client.set_host_power('ON') 123 self.rf_client.set_host_power('ON')
123 self.sushy.get_system().reset_system.assert_called_once_with( 124 self.sushy.get_system().reset_system.assert_called_once_with(
124 sushy.RESET_ON) 125 sushy.RESET_ON)
126
127 def test_press_pwr_btn(self):
128 self.rf_client.press_pwr_btn()
129 self.sushy.get_system().push_power_button.assert_called_once_with(
130 sys_cons.PUSH_POWER_BUTTON_PRESS)
131
132 def test_press_pwr_btn_fail(self):
133 self.sushy.get_system().push_power_button.side_effect = (
134 sushy.exceptions.SushyError)
135 self.assertRaisesRegex(
136 exception.IloError,
137 'The Redfish controller failed to press power button',
138 self.rf_client.press_pwr_btn)
139
140 def test_hold_pwr_btn(self):
141 self.rf_client.hold_pwr_btn()
142 self.sushy.get_system().push_power_button.assert_called_once_with(
143 sys_cons.PUSH_POWER_BUTTON_PRESS_AND_HOLD)
144
145 def test_hold_pwr_btn_fail(self):
146 self.sushy.get_system().push_power_button.side_effect = (
147 sushy.exceptions.SushyError)
148 self.assertRaisesRegex(
149 exception.IloError,
150 'The Redfish controller failed to press and hold power button',
151 self.rf_client.hold_pwr_btn)