diff --git a/ironic/drivers/modules/irmc/management.py b/ironic/drivers/modules/irmc/management.py index 077ebf7e5a..c46cfd10c6 100644 --- a/ironic/drivers/modules/irmc/management.py +++ b/ironic/drivers/modules/irmc/management.py @@ -35,7 +35,12 @@ LOG = logging.getLogger(__name__) # Set/Get System Boot Options Command, IPMI spec v2.0. _BOOTPARAM5_DATA2 = {boot_devices.PXE: '0x04', boot_devices.DISK: '0x08', - boot_devices.CDROM: '0x14', + # note (naohirot) + # boot_devices.CDROM is tentatively set to '0x20' rather + # than '0x14' as a work-around to force iRMC vmedia boot. + # 0x14 = Force boot from default CD/DVD + # 0x20 = Force boot from remotely connected CD/DVD + boot_devices.CDROM: '0x20', boot_devices.BIOS: '0x18', boot_devices.SAFE: '0x0c', } @@ -141,33 +146,37 @@ class IRMCManagement(ipmitool.IPMIManagement): :raises: IPMIFailure on an error from ipmitool. """ - if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi': - if device not in self.get_supported_boot_devices(task): - raise exception.InvalidParameterValue(_( - "Invalid boot device %s specified.") % device) - timeout_disable = "0x00 0x08 0x03 0x08" - ipmitool.send_raw(task, timeout_disable) + if device not in self.get_supported_boot_devices(task): + raise exception.InvalidParameterValue(_( + "Invalid boot device %s specified.") % device) - # note(naohirot): As of ipmitool version 1.8.13, - # in case of chassis command, the efiboot option doesn't - # get set with persistent at the same time. - # $ ipmitool chassis bootdev pxe options=efiboot,persistent - # In case of raw command, however, both can be set at the - # same time. - # $ ipmitool raw 0x00 0x08 0x05 0xe0 0x04 0x00 0x00 0x00 - # data1^^ ^^data2 - # ipmi cmd '0x08' : Set System Boot Options - # data1 '0xe0' : persistent and uefi - # data1 '0xa0' : next boot only and uefi - # - data1 = '0xe0' if persistent else '0xa0' - bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00' - cmd08 = bootparam5 % (data1, _BOOTPARAM5_DATA2[device]) - ipmitool.send_raw(task, cmd08) + uefi_mode = ( + driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi') + # disable 60 secs timer + timeout_disable = "0x00 0x08 0x03 0x08" + ipmitool.send_raw(task, timeout_disable) + + # note(naohirot): + # Set System Boot Options : ipmi cmd '0x08', bootparam '0x05' + # + # $ ipmitool raw 0x00 0x08 0x05 data1 data2 0x00 0x00 0x00 + # + # data1 : '0xe0' persistent + uefi + # '0xc0' persistent + bios + # '0xa0' next only + uefi + # '0x80' next only + bios + # data2 : boot device defined in the dict _BOOTPARAM5_DATA2 + + bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00' + if persistent: + data1 = '0xe0' if uefi_mode else '0xc0' else: - super(IRMCManagement, self).set_boot_device( - task, device, persistent) + data1 = '0xa0' if uefi_mode else '0x80' + data2 = _BOOTPARAM5_DATA2[device] + + cmd8 = bootparam5 % (data1, data2) + ipmitool.send_raw(task, cmd8) def get_sensors_data(self, task): """Get sensors data method. diff --git a/ironic/tests/unit/drivers/modules/irmc/test_management.py b/ironic/tests/unit/drivers/modules/irmc/test_management.py index 687ea1f542..fb75b394f0 100644 --- a/ironic/tests/unit/drivers/modules/irmc/test_management.py +++ b/ironic/tests/unit/drivers/modules/irmc/test_management.py @@ -86,108 +86,169 @@ class IRMCManagementTestCase(db_base.DbTestCase): self.assertEqual(sorted(expected), sorted(task.driver.management. get_supported_boot_devices(task))) - @mock.patch.object(ipmitool.IPMIManagement, 'set_boot_device', - spec_set=True, autospec=True) - def test_management_interface_set_boot_device_no_mode_ok( - self, - set_boot_device_mock): - """no boot mode specified.""" - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.management.set_boot_device(task, boot_devices.PXE) - set_boot_device_mock.assert_called_once_with( - task.driver.management, task, - boot_devices.PXE, - False) - - @mock.patch.object(ipmitool.IPMIManagement, 'set_boot_device', - spec_set=True, autospec=True) - def test_management_interface_set_boot_device_bios_ok( - self, - set_boot_device_mock): - """bios mode specified.""" - with task_manager.acquire(self.context, self.node.uuid) as task: - driver_utils.add_node_capability(task, 'boot_mode', 'bios') - task.driver.management.set_boot_device(task, boot_devices.PXE) - set_boot_device_mock.assert_called_once_with( - task.driver.management, task, - boot_devices.PXE, - False) - @mock.patch.object(irmc_management.ipmitool, "send_raw", spec_set=True, autospec=True) - def _test_management_interface_set_boot_device_uefi_ok(self, params, - expected_raw_code, - send_raw_mock): + def _test_management_interface_set_boot_device_ok( + self, boot_mode, params, expected_raw_code, send_raw_mock): send_raw_mock.return_value = [None, None] with task_manager.acquire(self.context, self.node.uuid) as task: task.node.properties['capabilities'] = '' - driver_utils.add_node_capability(task, 'boot_mode', 'uefi') + if boot_mode: + driver_utils.add_node_capability(task, 'boot_mode', boot_mode) self.driver.management.set_boot_device(task, **params) send_raw_mock.assert_has_calls([ mock.call(task, "0x00 0x08 0x03 0x08"), mock.call(task, expected_raw_code)]) - def test_management_interface_set_boot_device_uefi_ok_pxe(self): + def test_management_interface_set_boot_device_ok_pxe(self): params = {'device': boot_devices.PXE, 'persistent': False} - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0x80 0x04 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0x80 0x04 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xa0 0x04 0x00 0x00 0x00") params['persistent'] = True - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0xc0 0x04 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0xc0 0x04 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xe0 0x04 0x00 0x00 0x00") - def test_management_interface_set_boot_device_uefi_ok_disk(self): + def test_management_interface_set_boot_device_ok_disk(self): params = {'device': boot_devices.DISK, 'persistent': False} - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0x80 0x08 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0x80 0x08 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xa0 0x08 0x00 0x00 0x00") params['persistent'] = True - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0xc0 0x08 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0xc0 0x08 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xe0 0x08 0x00 0x00 0x00") - def test_management_interface_set_boot_device_uefi_ok_cdrom(self): + def test_management_interface_set_boot_device_ok_cdrom(self): params = {'device': boot_devices.CDROM, 'persistent': False} - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, params, - "0x00 0x08 0x05 0xa0 0x14 0x00 0x00 0x00") + "0x00 0x08 0x05 0x80 0x20 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0x80 0x20 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', + params, + "0x00 0x08 0x05 0xa0 0x20 0x00 0x00 0x00") params['persistent'] = True - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, params, - "0x00 0x08 0x05 0xe0 0x14 0x00 0x00 0x00") + "0x00 0x08 0x05 0xc0 0x20 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0xc0 0x20 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', + params, + "0x00 0x08 0x05 0xe0 0x20 0x00 0x00 0x00") - def test_management_interface_set_boot_device_uefi_ok_bios(self): + def test_management_interface_set_boot_device_ok_bios(self): params = {'device': boot_devices.BIOS, 'persistent': False} - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0x80 0x18 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0x80 0x18 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xa0 0x18 0x00 0x00 0x00") params['persistent'] = True - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0xc0 0x18 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0xc0 0x18 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xe0 0x18 0x00 0x00 0x00") - def test_management_interface_set_boot_device_uefi_ok_safe(self): + def test_management_interface_set_boot_device_ok_safe(self): params = {'device': boot_devices.SAFE, 'persistent': False} - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0x80 0x0c 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0x80 0x0c 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xa0 0x0c 0x00 0x00 0x00") params['persistent'] = True - self._test_management_interface_set_boot_device_uefi_ok( + self._test_management_interface_set_boot_device_ok( + None, + params, + "0x00 0x08 0x05 0xc0 0x0c 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'bios', + params, + "0x00 0x08 0x05 0xc0 0x0c 0x00 0x00 0x00") + self._test_management_interface_set_boot_device_ok( + 'uefi', params, "0x00 0x08 0x05 0xe0 0x0c 0x00 0x00 0x00") @mock.patch.object(irmc_management.ipmitool, "send_raw", spec_set=True, autospec=True) - def test_management_interface_set_boot_device_uefi_ng(self, - send_raw_mock): + def test_management_interface_set_boot_device_ng(self, send_raw_mock): """uefi mode, next boot only, unknown device.""" send_raw_mock.return_value = [None, None] diff --git a/releasenotes/notes/update-irmc-set-boot-device-fd50d9dce42aaa89.yaml b/releasenotes/notes/update-irmc-set-boot-device-fd50d9dce42aaa89.yaml new file mode 100644 index 0000000000..c758b72239 --- /dev/null +++ b/releasenotes/notes/update-irmc-set-boot-device-fd50d9dce42aaa89.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - This forces iRMC vmedia boot from remotely connected (redirected) + CD/DVD instead of default CD/DVD. See + https://bugs.launchpad.net/ironic/+bug/1561852 for details.