diff options
author | Julia Kreger <juliaashleykreger@gmail.com> | 2020-10-09 17:12:07 -0700 |
---|---|---|
committer | Dmitry Tantsur <dtantsur@protonmail.com> | 2020-12-14 12:00:38 +0000 |
commit | a7ac9ce8cd53fdebdf0d9aaeaf439da08a0b3ec8 (patch) | |
tree | 646165c509ba7dbea9a545439a3908fc00c0c77a /ironic/tests | |
parent | cde792a8c3b36afd9dd916b38bf60ed6af4f441a (diff) | |
download | ironic-a7ac9ce8cd53fdebdf0d9aaeaf439da08a0b3ec8.tar.gz |
IPMI: Handle vendor set boot device differences
Supermicro machines, when in UEFI mode, have a different
device number, in binary, to represent the hard disk from
other vendors such as Fujitsu which actually has somewhat
similar code in their driver.
This means we need to be somewhat cognizent of the vendor of
the BMC and possibly update the device mapping based upon that
vendor.
This may ultimately fix a number of IPMI related problems, because
there is a reliance upon the text output of ipmitool, which only
reads the bytes retured by the BMC, which may not be reality after
the next reset, espescialy if ipmitool doesn't know of the UEFI
operating difference.
Change-Id: Ie19db9e0cf1eafdfc9bb46248f4d457337821f94
Story: 2008241
Task: 41085
Diffstat (limited to 'ironic/tests')
-rw-r--r-- | ironic/tests/unit/common/test_neutron.py | 1 | ||||
-rw-r--r-- | ironic/tests/unit/drivers/modules/test_ipmitool.py | 103 |
2 files changed, 98 insertions, 6 deletions
diff --git a/ironic/tests/unit/common/test_neutron.py b/ironic/tests/unit/common/test_neutron.py index 07dcb290e..ec953e927 100644 --- a/ironic/tests/unit/common/test_neutron.py +++ b/ironic/tests/unit/common/test_neutron.py @@ -30,6 +30,7 @@ from ironic.tests.unit.db import base as db_base from ironic.tests.unit.objects import utils as object_utils from ironic.tests.unit import stubs + @mock.patch('ironic.common.keystone.get_service_auth', autospec=True, return_value=mock.sentinel.sauth) @mock.patch('ironic.common.keystone.get_auth', autospec=True, diff --git a/ironic/tests/unit/drivers/modules/test_ipmitool.py b/ironic/tests/unit/drivers/modules/test_ipmitool.py index 3c7ad07a7..b09dbead6 100644 --- a/ironic/tests/unit/drivers/modules/test_ipmitool.py +++ b/ironic/tests/unit/drivers/modules/test_ipmitool.py @@ -58,6 +58,31 @@ INFO_DICT = db_utils.get_test_ipmi_info() BRIDGE_INFO_DICT = INFO_DICT.copy() BRIDGE_INFO_DICT.update(db_utils.get_test_ipmi_bridging_parameters()) +MC_INFO = """Device ID : 32 +Device Revision : 1 +Firmware Revision : 0.99 +IPMI Version : 2.0 +Manufacturer ID : 1234 +Manufacturer Name : {vendor} +Product ID : 2001 (0x0801) +Product Name : Unknown (0x101) +Device Available : yes +Provides Device SDRs : no +Additional Device Support : + Sensor Device + SDR Repository Device + SEL Device + FRU Inventory Device + IPMB Event Receiver + IPMB Event Generator + Chassis Device +Aux Firmware Rev Info : + 0x00 + 0x00 + 0x00 + 0x00 +""" + class IPMIToolCheckInitTestCase(base.TestCase): @@ -1510,20 +1535,22 @@ class IPMIToolPrivateMethodTestCase( mock.call(self.info, "power status", kill_on_timeout=True)] with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['vendor'] = 'lolcat' self.assertRaises(exception.PowerStateFailure, ipmi._power_on, task, self.info, timeout=2) self.assertEqual(expected, mock_exec.call_args_list) + @mock.patch.object(ipmi.IPMIManagement, 'detect_vendor', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) @mock.patch('oslo_utils.eventletutils.EventletEvent.wait', autospec=True) - def test__soft_power_off(self, sleep_mock, mock_exec): - + def test__soft_power_off(self, sleep_mock, mock_exec, mock_vendor): def side_effect(driver_info, command, **kwargs): resp_dict = {"power status": ["Chassis Power is off\n", None], "power soft": [None, None]} return resp_dict.get(command, ["Bad\n", None]) + mock_vendor.return_value = None mock_exec.side_effect = side_effect expected = [mock.call(self.info, "power soft"), @@ -1534,10 +1561,13 @@ class IPMIToolPrivateMethodTestCase( self.assertEqual(expected, mock_exec.call_args_list) self.assertEqual(states.POWER_OFF, state) + self.assertTrue(mock_vendor.called) + @mock.patch.object(ipmi.IPMIManagement, 'detect_vendor', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) @mock.patch('oslo_utils.eventletutils.EventletEvent.wait', autospec=True) - def test__soft_power_off_max_retries(self, sleep_mock, mock_exec): + def test__soft_power_off_max_retries(self, sleep_mock, mock_exec, + mock_vendor): def side_effect(driver_info, command, **kwargs): resp_dict = {"power status": ["Chassis Power is on\n", None], @@ -1551,10 +1581,13 @@ class IPMIToolPrivateMethodTestCase( mock.call(self.info, "power status", kill_on_timeout=True)] with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['vendor'] = 'meow5000' self.assertRaises(exception.PowerStateFailure, ipmi._soft_power_off, task, self.info, timeout=2) self.assertEqual(expected, mock_exec.call_args_list) + # Should be removed when detect_vendor automatic invocation is moved. + self.assertFalse(mock_vendor.called) @mock.patch.object(ipmi, '_power_status', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) @@ -1594,8 +1627,9 @@ class IPMIToolDriverTestCase(Base): self.assertEqual(sorted(expected), sorted(task.driver.get_properties())) + @mock.patch.object(ipmi.IPMIManagement, 'detect_vendor', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_get_power_state(self, mock_exec): + def test_get_power_state(self, mock_exec, mock_detect): returns = iter([["Chassis Power is off\n", None], ["Chassis Power is on\n", None], ["\n", None]]) @@ -1603,6 +1637,7 @@ class IPMIToolDriverTestCase(Base): mock.call(self.info, "power status", kill_on_timeout=True), mock.call(self.info, "power status", kill_on_timeout=True)] mock_exec.side_effect = returns + mock_detect.return_value = None with task_manager.acquire(self.context, self.node.uuid) as task: pstate = self.power.get_power_state(task) @@ -1615,16 +1650,20 @@ class IPMIToolDriverTestCase(Base): self.assertEqual(states.ERROR, pstate) self.assertEqual(mock_exec.call_args_list, expected) + self.assertEqual(3, mock_detect.call_count) + @mock.patch.object(ipmi.IPMIManagement, 'detect_vendor', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_get_power_state_exception(self, mock_exec): + def test_get_power_state_exception(self, mock_exec, mock_vendor): mock_exec.side_effect = processutils.ProcessExecutionError("error") + mock_vendor.return_value = None with task_manager.acquire(self.context, self.node.uuid) as task: self.assertRaises(exception.IPMIFailure, self.power.get_power_state, task) mock_exec.assert_called_once_with(self.info, "power status", kill_on_timeout=True) + self.assertEqual(1, mock_vendor.call_count) @mock.patch.object(ipmi, '_power_on', autospec=True) @mock.patch.object(ipmi, '_power_off', autospec=True) @@ -2230,7 +2269,7 @@ class IPMIToolDriverTestCase(Base): mock_calls = [ mock.call(self.info, "raw 0x00 0x08 0x03 0x08"), - mock.call(self.info, "chassis bootdev pxe options=efiboot") + mock.call(self.info, "raw 0x00 0x08 0x05 0xa0 0x04 0x00 0x00 0x00") ] mock_exec.assert_has_calls(mock_calls) @@ -2251,6 +2290,48 @@ class IPMIToolDriverTestCase(Base): ] mock_exec.assert_has_calls(mock_calls) + @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', + autospec=True) + @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) + def test_management_interface_set_boot_device_uefi_and_persistent_smci( + self, mock_exec, mock_boot_mode): + mock_boot_mode.return_value = 'uefi' + mock_exec.return_value = [None, None] + properties = self.node.properties + properties['vendor'] = 'SuperMicro' + self.node.properties = properties + self.node.save() + with task_manager.acquire(self.context, self.node.uuid) as task: + self.management.set_boot_device(task, boot_devices.DISK, + persistent=True) + mock_calls = [ + mock.call(self.info, "raw 0x00 0x08 0x03 0x08"), + mock.call(self.info, "raw 0x00 0x08 0x05 0xe0 " + "0x24 0x00 0x00 0x00") + ] + mock_exec.assert_has_calls(mock_calls) + + @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', + autospec=True) + @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) + def test_management_interface_set_boot_device_uefi_and_onetime_smci( + self, mock_exec, mock_boot_mode): + mock_boot_mode.return_value = 'uefi' + mock_exec.return_value = [None, None] + properties = self.node.properties + properties['vendor'] = 'SuperMicro' + self.node.properties = properties + self.node.save() + with task_manager.acquire(self.context, self.node.uuid) as task: + self.management.set_boot_device(task, boot_devices.DISK, + persistent=False) + mock_calls = [ + mock.call(self.info, "raw 0x00 0x08 0x03 0x08"), + mock.call(self.info, "raw 0x00 0x08 0x05 0xa0 " + "0x24 0x00 0x00 0x00") + ] + mock_exec.assert_has_calls(mock_calls) + def test_management_interface_get_supported_boot_devices(self): with task_manager.acquire(self.context, self.node.uuid) as task: expected = [boot_devices.PXE, boot_devices.DISK, @@ -2366,6 +2447,16 @@ class IPMIToolDriverTestCase(Base): mock_exec.assert_called_once_with(driver_info, "power diag") self.assertTrue(mock_log.called) + @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) + def test_detect_vendor(self, mock_exec): + mock_exec.return_value = (MC_INFO.format(vendor='LolCatMeow'), + None) + with task_manager.acquire(self.context, self.node.uuid) as task: + driver_info = ipmi._parse_driver_info(task.node) + vendor = self.management.detect_vendor(task) + mock_exec.assert_called_once_with(driver_info, 'mc info') + self.assertEqual('lolcatmeow', vendor) + def test__parse_ipmi_sensor_data_ok(self): fake_sensors_data = """ Sensor ID : Temp (0x1) |