Add support for NMI(Non Maskable Interrupt)

This commit adds support for NMI for Gen9 and Gen10 servers.

Note: These is no RIBCL interface to perform NMI using RIBCL.

Change-Id: Iaf663443179417e47d022098c73a4776d41d4287
Partial-Bug: #1762311
This commit is contained in:
Shivanand Tendulker 2018-04-09 02:53:24 -04:00
parent 60910b9257
commit f3db9b7c8a
9 changed files with 139 additions and 1 deletions

View File

@ -39,6 +39,7 @@ SUPPORTED_RIS_METHODS = [
'get_supported_boot_mode',
'get_vm_status',
'hold_pwr_btn',
'inject_nmi',
'insert_virtual_media',
'press_pwr_btn',
'reset_bios_to_default',
@ -73,6 +74,7 @@ SUPPORTED_REDFISH_METHODS = [
'get_current_boot_mode',
'activate_license',
'eject_virtual_media',
'inject_nmi',
'insert_virtual_media',
'set_vm_status',
'update_firmware',
@ -641,3 +643,15 @@ class IloClient(operations.IloOperations):
"""
return self._call_method(
'update_firmware', firmware_url, component_type)
def inject_nmi(self):
"""Inject NMI, Non Maskable Interrupt.
Inject NMI (Non Maskable Interrupt) for a node immediately.
:raises: IloError, on an error from iLO
:raises: IloConnectionError, if not able to reach iLO.
:raises: IloCommandNotSupportedError, if the command is
not supported on the server
"""
return self._call_method('inject_nmi')

View File

@ -360,3 +360,15 @@ class IloOperations(object):
not supported on the server
"""
raise exception.IloCommandNotSupportedError(ERRMSG)
def inject_nmi(self):
"""Inject NMI, Non Maskable Interrupt.
Inject NMI (Non Maskable Interrupt) for a node immediately.
:raises: IloError, on an error from iLO
:raises: IloConnectionError, if not able to reach iLO.
:raises: IloCommandNotSupportedError, if the command is
not supported on the server
"""
raise exception.IloCommandNotSupportedError(ERRMSG)

View File

@ -1183,6 +1183,18 @@ class RIBCLOperations(operations.IloOperations):
if location == 'Embedded':
nic_dict[port] = mac
def inject_nmi(self):
"""Inject NMI, Non Maskable Interrupt.
Inject NMI (Non Maskable Interrupt) for a node immediately.
:raises: IloError, on an error from iLO
:raises: IloCommandNotSupportedError
"""
platform = self.get_product_name()
msg = ("`inject_nmi` is not supported on %s" % platform)
raise exception.IloCommandNotSupportedError(msg)
# The below block of code is there only for backward-compatibility
# reasons (before commit 47608b6 for ris-support).

View File

@ -789,7 +789,8 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations):
"""Perform requested power operation.
:param oper: Type of power button press to simulate.
Supported values: 'ON', 'ForceOff' and 'ForceRestart'
Supported values: 'ON', 'ForceOff', 'ForceRestart' and
'Nmi'
:raises: IloError, on an error from iLO.
"""
@ -1820,3 +1821,16 @@ class RISOperations(rest.RestConnectorBase, operations.IloOperations):
except exception.IloCommandNotSupportedError:
nvn_status = False
return nvn_status
def inject_nmi(self):
"""Inject NMI, Non Maskable Interrupt.
Inject NMI (Non Maskable Interrupt) for a node immediately.
:raises: IloError, on an error from iLO
"""
cur_status = self.get_host_power_status()
if cur_status != 'ON':
raise exception.IloError("Server is not in powered on state.")
self._perform_power_op("Nmi")

View File

@ -1006,3 +1006,22 @@ class RedfishOperations(operations.IloOperations):
else:
msg = 'iSCSI initiator cannot be retrieved in BIOS boot mode'
raise exception.IloCommandNotSupportedInBiosError(msg)
def inject_nmi(self):
"""Inject NMI, Non Maskable Interrupt.
Inject NMI (Non Maskable Interrupt) for a node immediately.
:raises: IloError, on an error from iLO
"""
sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID)
if sushy_system.power_state != sushy.SYSTEM_POWER_STATE_ON:
raise exception.IloError("Server is not in powered on state.")
try:
sushy_system.reset_system(sushy.RESET_NMI)
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller failed to inject nmi to '
'server. Error %(error)s') % {'error': str(e)})
LOG.debug(msg)
raise exception.IloError(msg)

View File

@ -1011,6 +1011,24 @@ class IloClientTestCase(testtools.TestCase):
call_mock.assert_called_once_with('get_essential_properties')
self.assertFalse(snmp_mock.called)
@mock.patch.object(client.IloClient, '_call_method')
def test_inject_nmi(self, call_mock):
self.client.inject_nmi()
call_mock.assert_called_once_with('inject_nmi')
@mock.patch.object(ris.RISOperations, 'inject_nmi')
def test_inject_nmi_gen9(self, inject_nmi_mock):
self.client.model = 'Gen9'
self.client.inject_nmi()
inject_nmi_mock.assert_called_once_with()
@mock.patch.object(ribcl.RIBCLOperations, 'get_product_name')
def test_inject_nmi_gen8(self, product_mock):
self.client.model = 'Gen8'
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
'not supported',
self.client.inject_nmi)
class IloRedfishClientTestCase(testtools.TestCase):

View File

@ -1041,6 +1041,13 @@ class IloRibclTestCaseBeforeRisSupport(unittest.TestCase):
self.ilo.set_vm_status('cdrom', 'boot_once', 'yes')
self.assertTrue(request_ilo_mock.called)
@mock.patch.object(ribcl.RIBCLOperations, 'get_product_name')
def test_inject_nmi(self, product_name_mock):
product_name_mock.return_value = constants.GET_PRODUCT_NAME
self.assertRaisesRegexp(exception.IloCommandNotSupportedError,
'ProLiant DL380 G7',
self.ilo.inject_nmi)
if __name__ == '__main__':
unittest.main()

View File

@ -1239,6 +1239,25 @@ class IloRisTestCase(testtools.TestCase):
self.client.hold_pwr_btn()
press_pwr_btn_mock.assert_called_once_with(pushType="PressAndHold")
@mock.patch.object(ris.RISOperations, '_perform_power_op')
@mock.patch.object(ris.RISOperations, 'get_host_power_status')
def test_inject_nmi(self, get_power_status_mock,
perform_power_op_mock):
get_power_status_mock.return_value = 'ON'
self.client.inject_nmi()
get_power_status_mock.assert_called_once_with()
perform_power_op_mock.assert_called_once_with('Nmi')
@mock.patch.object(ris.RISOperations, '_perform_power_op')
@mock.patch.object(ris.RISOperations, 'get_host_power_status')
def test_inject_nmi_exc(self, get_power_status_mock,
perform_power_op_mock):
get_power_status_mock.return_value = 'OFF'
self.assertRaises(exception.IloError,
self.client.inject_nmi)
get_power_status_mock.assert_called_once_with()
self.assertFalse(perform_power_op_mock.called)
class TestRISOperationsPrivateMethods(testtools.TestCase):

View File

@ -1419,3 +1419,26 @@ class RedfishOperationsTestCase(testtools.TestCase):
'iSCSI initiator cannot be retrieved in '
'BIOS boot mode',
self.rf_client.get_iscsi_initiator_info)
def test_inject_nmi(self):
self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_ON
self.rf_client.inject_nmi()
self.sushy.get_system().reset_system.assert_called_once_with(
sushy.RESET_NMI)
def test_inject_nmi_power_off(self):
self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_OFF
self.assertRaisesRegex(
exception.IloError,
'Server is not in powered on state.',
self.rf_client.inject_nmi)
self.assertFalse(self.sushy.get_system().reset_system.called)
def test_inject_nmi_sushy_exc(self):
self.sushy.get_system().power_state = sushy.SYSTEM_POWER_STATE_ON
self.sushy.get_system().reset_system.side_effect = (
sushy.exceptions.SushyError)
self.assertRaisesRegex(
exception.IloError,
'The Redfish controller failed to inject nmi',
self.rf_client.inject_nmi)