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
This commit is contained in:
Debayan Ray 2017-06-09 04:40:48 -04:00
parent 840f7d72f7
commit e2f1bb4167
8 changed files with 229 additions and 18 deletions

View File

@ -62,6 +62,8 @@ SUPPORTED_REDFISH_METHODS = [
'get_host_power_status',
'set_host_power',
'reset_server',
'press_pwr_btn',
'hold_pwr_btn',
]
LOG = log.get_logger(__name__)

View File

@ -21,7 +21,7 @@ from proliantutils import exception
from proliantutils.ilo import operations
from proliantutils import log
from proliantutils.redfish import main
from proliantutils.redfish.resources.system import constants as sys_cons
"""
Class specific for Redfish APIs.
@ -39,6 +39,8 @@ POWER_RESET_MAP = {
'OFF': sushy.RESET_FORCE_OFF,
}
# Assuming only one sushy_system present as part of collection,
# as we are dealing with iLO's here.
PROLIANT_SYSTEM_ID = '1'
LOG = log.get_logger(__name__)
@ -47,17 +49,11 @@ LOG = log.get_logger(__name__)
class RedfishOperations(operations.IloOperations):
"""Operations supported on redfish based hardware.
This is the list of APIs which are currently supported via Redfish mode
This class holds APIs which are currently supported via Redfish mode
of operation. This is a growing list which needs to be updated as and when
the existing API/s (of its cousin RIS and RIBCL interfaces) are migrated.
--- START ---
* get_product_name(self)
* get_host_power_status(self)
--- END ---
For operations currently supported on the client object, please refer:
*proliantutils.ilo.client.SUPPORTED_REDFISH_METHODS*
"""
def __init__(self, redfish_controller_ip, username, password,
@ -129,8 +125,6 @@ class RedfishOperations(operations.IloOperations):
:returns: server model name.
:raises: IloError, on an error from iLO.
"""
# Assuming only one system present as part of collection,
# as we are dealing with iLO's here.
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
return sushy_system.json.get('Model')
@ -140,8 +134,6 @@ class RedfishOperations(operations.IloOperations):
:returns: Power State of the server, 'ON' or 'OFF'
:raises: IloError, on an error from iLO.
"""
# Assuming only one sushy_system present as part of collection,
# as we are dealing with iLO's here.
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
return GET_POWER_STATE_MAP.get(sushy_system.power_state)
@ -150,8 +142,6 @@ class RedfishOperations(operations.IloOperations):
:raises: IloError, on an error from iLO.
"""
# Assuming only one sushy_system present as part of collection,
# as we are dealing with iLO's here.
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
try:
sushy_system.reset_system(sushy.RESET_FORCE_RESTART)
@ -185,8 +175,6 @@ class RedfishOperations(operations.IloOperations):
"state."), {'target_value': target_value})
return
# Assuming only one system present as part of collection,
# as we are dealing with iLO's here.
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
try:
sushy_system.reset_system(POWER_RESET_MAP[target_value])
@ -196,3 +184,34 @@ class RedfishOperations(operations.IloOperations):
{'target_value': target_value, 'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
def press_pwr_btn(self):
"""Simulates a physical press of the server power button.
:raises: IloError, on an error from iLO.
"""
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
try:
sushy_system.push_power_button(sys_cons.PUSH_POWER_BUTTON_PRESS)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller failed to press power button'
' of server. Error %(error)s') %
{'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)
def hold_pwr_btn(self):
"""Simulate a physical press and hold of the server power button.
:raises: IloError, on an error from iLO.
"""
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
try:
sushy_system.push_power_button(
sys_cons.PUSH_POWER_BUTTON_PRESS_AND_HOLD)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller failed to press and hold '
'power button of server. Error %(error)s') %
{'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)

View File

@ -0,0 +1,20 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Push power button action constants
PUSH_POWER_BUTTON_PRESS = 'press'
PUSH_POWER_BUTTON_PRESS_AND_HOLD = 'press and hold'

View File

@ -0,0 +1,27 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sushy import utils
from proliantutils.redfish.resources.system import constants
PUSH_POWER_BUTTON_VALUE_MAP = {
'Press': constants.PUSH_POWER_BUTTON_PRESS,
'PressAndHold': constants.PUSH_POWER_BUTTON_PRESS_AND_HOLD,
}
PUSH_POWER_BUTTON_VALUE_MAP_REV = (
utils.revert_dictionary(PUSH_POWER_BUTTON_VALUE_MAP))

View File

@ -14,8 +14,27 @@
__author__ = 'HPE'
from sushy.resources import base
from sushy.resources.system import system
from proliantutils import exception
from proliantutils import log
from proliantutils.redfish.resources.system import mappings
LOG = log.get_logger(__name__)
class PowerButtonActionField(base.CompositeField):
allowed_values = base.Field('PushType@Redfish.AllowableValues',
adapter=list)
target_uri = base.Field('target', required=True)
class HpeActionsField(base.CompositeField):
computer_system_ext_powerbutton = (
PowerButtonActionField('#HpeComputerSystemExt.PowerButton'))
class HPESystem(system.System):
"""Class that extends the functionality of System resource class
@ -23,3 +42,37 @@ class HPESystem(system.System):
This class extends the functionality of System resource class
from sushy
"""
_hpe_actions = HpeActionsField(['Oem', 'Hpe', 'Actions'], required=True)
"""Oem specific system extensibility actions"""
def _get_hpe_push_power_button_action_element(self):
push_action = self._hpe_actions.computer_system_ext_powerbutton
if not push_action:
raise exception.MissingAttributeError(
attribute='Oem/Hpe/Actions/#HpeComputerSystemExt.PowerButton',
resource=self.path)
return push_action
def push_power_button(self, target_value):
"""Reset the system in hpe exclusive manner.
:param target_value: The target value to be set.
:raises: InvalidInputError, if the target value is not
allowed.
:raises: SushyError, on an error from iLO.
"""
if target_value not in mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV:
msg = ('The parameter "%(parameter)s" value "%(target_value)s" is '
'invalid. Valid values are: %(valid_power_values)s' %
{'parameter': 'target_value', 'target_value': target_value,
'valid_power_values': (
mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV.keys())})
raise exception.InvalidInputError(msg)
value = mappings.PUSH_POWER_BUTTON_VALUE_MAP_REV[target_value]
target_uri = (
self._get_hpe_push_power_button_action_element().target_uri)
self._conn.post(target_uri, data={'PushType': value})

View File

@ -0,0 +1,63 @@
# Copyright 2017 Hewlett Packard Enterprise Development LP
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import mock
import testtools
from proliantutils import exception
from proliantutils.redfish.resources.system import constants as sys_cons
from proliantutils.redfish.resources.system import system
class HPESystemTestCase(testtools.TestCase):
def setUp(self):
super(HPESystemTestCase, self).setUp()
self.conn = mock.MagicMock()
with open('proliantutils/tests/redfish/'
'json_samples/system.json', 'r') as f:
self.conn.get.return_value.json.return_value = json.loads(f.read())
self.sys_inst = system.HPESystem(
self.conn, '/redfish/v1/Systems/1',
redfish_version='1.0.2')
def test__get_hpe_push_power_button_action_element(self):
value = self.sys_inst._get_hpe_push_power_button_action_element()
self.assertEqual("/redfish/v1/Systems/1/Actions/Oem/Hpe/"
"HpeComputerSystemExt.PowerButton/",
value.target_uri)
self.assertEqual(["Press", "PressAndHold"], value.allowed_values)
def test__get_hpe_push_power_button_action_element_missing_action(self):
self.sys_inst._hpe_actions.computer_system_ext_powerbutton = None
self.assertRaisesRegex(
exception.MissingAttributeError,
'Oem/Hpe/Actions/#HpeComputerSystemExt.PowerButton is missing',
self.sys_inst._get_hpe_push_power_button_action_element)
def test_push_power_button(self):
self.sys_inst.push_power_button(
sys_cons.PUSH_POWER_BUTTON_PRESS)
self.sys_inst._conn.post.assert_called_once_with(
'/redfish/v1/Systems/1/Actions/Oem/Hpe/'
'HpeComputerSystemExt.PowerButton/',
data={'PushType': 'Press'})
def test_push_power_button_invalid_value(self):
self.assertRaises(exception.InvalidInputError,
self.sys_inst.push_power_button, 'invalid-value')

View File

@ -22,6 +22,7 @@ import testtools
from proliantutils import exception
from proliantutils.redfish import main
from proliantutils.redfish import redfish
from proliantutils.redfish.resources.system import constants as sys_cons
class RedfishOperationsTestCase(testtools.TestCase):
@ -122,3 +123,29 @@ class RedfishOperationsTestCase(testtools.TestCase):
self.rf_client.set_host_power('ON')
self.sushy.get_system().reset_system.assert_called_once_with(
sushy.RESET_ON)
def test_press_pwr_btn(self):
self.rf_client.press_pwr_btn()
self.sushy.get_system().push_power_button.assert_called_once_with(
sys_cons.PUSH_POWER_BUTTON_PRESS)
def test_press_pwr_btn_fail(self):
self.sushy.get_system().push_power_button.side_effect = (
sushy.exceptions.SushyError)
self.assertRaisesRegex(
exception.IloError,
'The Redfish controller failed to press power button',
self.rf_client.press_pwr_btn)
def test_hold_pwr_btn(self):
self.rf_client.hold_pwr_btn()
self.sushy.get_system().push_power_button.assert_called_once_with(
sys_cons.PUSH_POWER_BUTTON_PRESS_AND_HOLD)
def test_hold_pwr_btn_fail(self):
self.sushy.get_system().push_power_button.side_effect = (
sushy.exceptions.SushyError)
self.assertRaisesRegex(
exception.IloError,
'The Redfish controller failed to press and hold power button',
self.rf_client.hold_pwr_btn)