diff --git a/devstack/common_settings b/devstack/common_settings index d649f70a59..8d5660c4df 100644 --- a/devstack/common_settings +++ b/devstack/common_settings @@ -5,8 +5,8 @@ if [[ -f $TOP_DIR/../../old/devstack/.localrc.auto ]]; then fi # Whether configure the nodes to boot in Legacy BIOS or UEFI mode. Accepted -# values are: "bios" or "uefi", defaults to "bios". -IRONIC_BOOT_MODE=${IRONIC_BOOT_MODE:-bios} +# values are: "bios" or "uefi", defaults to "uefi". +IRONIC_BOOT_MODE=${IRONIC_BOOT_MODE:-uefi} CIRROS_VERSION_DEVSTACK=$(set +o xtrace && source $TOP_DIR/stackrc && diff --git a/devstack/lib/ironic b/devstack/lib/ironic index 5aec60a15c..5fa1db08f7 100644 --- a/devstack/lib/ironic +++ b/devstack/lib/ironic @@ -2371,6 +2371,9 @@ function enroll_nodes { if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then node_capabilities+=" --property capabilities=boot_mode:uefi" fi + if [[ "$IRONIC_BOOT_MODE" == "bios" ]]; then + node_capabilities+=" --property capabilities=boot_mode:bios" + fi if [[ "$IRONIC_SECURE_BOOT" == "True" ]]; then if [[ -n "$node_capabilities" ]]; then node_capabilities+=",secure_boot:true" @@ -2581,7 +2584,9 @@ function enroll_nodes { if [[ "$IRONIC_BOOT_MODE" == "uefi" ]]; then openstack --os-cloud $OS_CLOUD flavor set baremetal --property "capabilities:boot_mode"="uefi" fi - + if [[ "$IRONIC_BOOT_MODE" == "bios" ]]; then + openstack --os-cloud $OS_CLOUD flavor set baremetal --property "capabilities:boot_mode"="bios" + fi for trait in $IRONIC_DEFAULT_TRAITS; do openstack --os-cloud $OS_CLOUD flavor set baremetal --property "trait:$trait"="required" done diff --git a/doc/source/install/include/boot-mode.inc b/doc/source/install/include/boot-mode.inc index 622c6cc71a..1beb7dac08 100644 --- a/doc/source/install/include/boot-mode.inc +++ b/doc/source/install/include/boot-mode.inc @@ -63,6 +63,16 @@ following way: openstack baremetal node set --property capabilities='boot_mode:uefi' + Conversely, to configure a node in ``bios`` mode, then set the + ``capabilities`` as below:: + + openstack baremetal node set --property capabilities='boot_mode:bios' + +.. note:: + + The Ironic project changed the default boot mode setting for nodes from + ``bios`` to ``uefi`` during the Yoga development cycle. + Nodes having ``boot_mode`` set to ``uefi`` may be requested by adding an ``extra_spec`` to the Compute service flavor:: diff --git a/ironic/conf/deploy.py b/ironic/conf/deploy.py index 5729be74fc..32f53644aa 100644 --- a/ironic/conf/deploy.py +++ b/ironic/conf/deploy.py @@ -132,14 +132,17 @@ opts = [ cfg.StrOpt('default_boot_mode', choices=[(boot_modes.UEFI, _('UEFI boot mode')), (boot_modes.LEGACY_BIOS, _('Legacy BIOS boot mode'))], - default=boot_modes.LEGACY_BIOS, + default=boot_modes.UEFI, mutable=True, help=_('Default boot mode to use when no boot mode is ' 'requested in node\'s driver_info, capabilities or ' 'in the `instance_info` configuration. Currently the ' - 'default boot mode is "%(bios)s", but it will be ' - 'changed to "%(uefi)s in the future. It is recommended ' - 'to set an explicit value for this option. This option ' + 'default boot mode is "%(uefi)s", but it was ' + '"%(bios)s" previously in Ironic. It is recommended ' + 'to set an explicit value for this option, and if the ' + 'setting or default differs from nodes, to ensure that ' + 'nodes are configured specifically for their desired ' + 'boot mode. This option ' 'only has effect when management interface supports ' 'boot mode management') % { 'bios': boot_modes.LEGACY_BIOS, diff --git a/ironic/drivers/modules/boot_mode_utils.py b/ironic/drivers/modules/boot_mode_utils.py index b88e6d3db3..6150a71bfa 100644 --- a/ironic/drivers/modules/boot_mode_utils.py +++ b/ironic/drivers/modules/boot_mode_utils.py @@ -16,7 +16,6 @@ from oslo_log import log as logging from oslo_utils import excutils -from ironic.common import boot_modes from ironic.common import exception from ironic.common.i18n import _ from ironic.common import utils as common_utils @@ -293,19 +292,6 @@ def get_boot_mode(node): boot_mode = get_boot_mode_for_deploy(node) if boot_mode: return boot_mode - # TODO(hshiina): The default boot mode will be changed to UEFI. - global warn_about_default_boot_mode - if (not warn_about_default_boot_mode - and CONF.deploy.default_boot_mode == boot_modes.LEGACY_BIOS): - warn_about_default_boot_mode = True - LOG.warning('Boot mode is not configured for node %(node_uuid)s ' - 'explicitly. The default boot mode is "%(bios)s", but, ' - 'the default will be changed to "%(uefi)s" in the future. ' - 'It is recommended to set the boot option into ' - 'properties/capabilities/boot_mode for all nodes.', - {'node_uuid': node.uuid, - 'bios': boot_modes.LEGACY_BIOS, - 'uefi': boot_modes.UEFI}) return CONF.deploy.default_boot_mode diff --git a/ironic/drivers/modules/deploy_utils.py b/ironic/drivers/modules/deploy_utils.py index 0124516512..37b8185318 100644 --- a/ironic/drivers/modules/deploy_utils.py +++ b/ironic/drivers/modules/deploy_utils.py @@ -608,7 +608,7 @@ def get_boot_option(node): :raises: InvalidParameterValue if the capabilities string is not a dict or is malformed. :returns: A string representing the boot option type. Defaults to - 'netboot'. + configuration setting [deploy]default_boot_mode. """ # NOTE(TheJulia): Software raid always implies local deployment diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py index 3b2ff0cf38..2ae878edb8 100644 --- a/ironic/tests/unit/common/test_pxe_utils.py +++ b/ironic/tests/unit/common/test_pxe_utils.py @@ -438,12 +438,14 @@ class TestPXEUtils(db_base.DbTestCase): unlink_mock.assert_called_once_with('/tftpboot/10.10.0.1.conf') create_link_mock.assert_has_calls(create_link_calls) + @mock.patch.object(pxe_utils, '_link_ip_address_pxe_configs', + autospec=True) @mock.patch.object(os, 'chmod', autospec=True) @mock.patch('ironic.common.utils.write_to_file', autospec=True) @mock.patch('ironic.common.utils.render_template', autospec=True) @mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True) def test_create_pxe_config(self, ensure_tree_mock, render_mock, - write_mock, chmod_mock): + write_mock, chmod_mock, mock_link_ip_addr): self.config(tftp_root=tempfile.mkdtemp(), group='pxe') with task_manager.acquire(self.context, self.node.uuid) as task: pxe_utils.create_pxe_config(task, self.pxe_options, @@ -451,8 +453,8 @@ class TestPXEUtils(db_base.DbTestCase): render_mock.assert_called_with( CONF.pxe.pxe_config_template, {'pxe_options': self.pxe_options, - 'ROOT': '{{ ROOT }}', - 'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'} + 'ROOT': '(( ROOT ))', + 'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'} ) node_dir = os.path.join(CONF.pxe.tftp_root, self.node.uuid) pxe_dir = os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg') @@ -465,14 +467,18 @@ class TestPXEUtils(db_base.DbTestCase): pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid) write_mock.assert_called_with(pxe_cfg_file_path, render_mock.return_value) + self.assertTrue(mock_link_ip_addr.called) + @mock.patch.object(pxe_utils, '_link_ip_address_pxe_configs', + autospec=True) @mock.patch.object(os, 'chmod', autospec=True) @mock.patch('ironic.common.utils.write_to_file', autospec=True) @mock.patch('ironic.common.utils.render_template', autospec=True) @mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True) def test_create_pxe_config_set_dir_permission(self, ensure_tree_mock, render_mock, - write_mock, chmod_mock): + write_mock, chmod_mock, + mock_link_ip_addr): self.config(tftp_root=tempfile.mkdtemp(), group='pxe') self.config(dir_permission=0o755, group='pxe') with task_manager.acquire(self.context, self.node.uuid) as task: @@ -481,8 +487,8 @@ class TestPXEUtils(db_base.DbTestCase): render_mock.assert_called_with( CONF.pxe.pxe_config_template, {'pxe_options': self.pxe_options, - 'ROOT': '{{ ROOT }}', - 'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'} + 'ROOT': '(( ROOT ))', + 'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'} ) node_dir = os.path.join(CONF.pxe.tftp_root, self.node.uuid) pxe_dir = os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg') @@ -496,17 +502,51 @@ class TestPXEUtils(db_base.DbTestCase): pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid) write_mock.assert_called_with(pxe_cfg_file_path, render_mock.return_value) + self.assertTrue(mock_link_ip_addr.called) + + @mock.patch.object(pxe_utils, '_link_ip_address_pxe_configs', + autospec=True) + @mock.patch.object(os.path, 'isdir', autospec=True) + @mock.patch.object(os, 'chmod', autospec=True) + @mock.patch('ironic.common.utils.write_to_file', autospec=True) + @mock.patch('ironic.common.utils.render_template', autospec=True) + @mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True) + def test_create_pxe_config_existing_dirs_uefi( + self, ensure_tree_mock, + render_mock, + write_mock, chmod_mock, + isdir_mock, mock_link_ip_address): + self.config(dir_permission=0o755, group='pxe') + with task_manager.acquire(self.context, self.node.uuid) as task: + isdir_mock.return_value = True + pxe_utils.create_pxe_config(task, self.pxe_options, + CONF.pxe.pxe_config_template) + render_mock.assert_called_with( + CONF.pxe.pxe_config_template, + {'pxe_options': self.pxe_options, + 'ROOT': '(( ROOT ))', + 'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'} + ) + ensure_tree_mock.assert_has_calls([]) + chmod_mock.assert_not_called() + isdir_mock.assert_has_calls([]) + pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid) + write_mock.assert_called_with(pxe_cfg_file_path, + render_mock.return_value) + self.assertTrue(mock_link_ip_address.called) @mock.patch.object(os.path, 'isdir', autospec=True) @mock.patch.object(os, 'chmod', autospec=True) @mock.patch('ironic.common.utils.write_to_file', autospec=True) @mock.patch('ironic.common.utils.render_template', autospec=True) @mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True) - def test_create_pxe_config_existing_dirs(self, ensure_tree_mock, - render_mock, - write_mock, chmod_mock, - isdir_mock): + def test_create_pxe_config_existing_dirs_bios( + self, ensure_tree_mock, + render_mock, + write_mock, chmod_mock, + isdir_mock): self.config(dir_permission=0o755, group='pxe') + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: isdir_mock.return_value = True pxe_utils.create_pxe_config(task, self.pxe_options, @@ -634,9 +674,12 @@ class TestPXEUtils(db_base.DbTestCase): write_mock.assert_called_with(pxe_cfg_file_path, render_mock.return_value) + @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.get_ip_addresses', + autospec=True) @mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True) @mock.patch('ironic_lib.utils.unlink_without_raise', autospec=True) - def test_clean_up_pxe_config(self, unlink_mock, rmtree_mock): + def test_clean_up_pxe_config(self, unlink_mock, rmtree_mock, + mock_get_ip_addr): address = "aa:aa:aa:aa:aa:aa" object_utils.create_test_port(self.context, node_id=self.node.id, address=address) @@ -654,6 +697,7 @@ class TestPXEUtils(db_base.DbTestCase): unlink_mock.assert_has_calls(ensure_calls) rmtree_mock.assert_called_once_with( os.path.join(CONF.pxe.tftp_root, self.node.uuid)) + self.assertTrue(mock_get_ip_addr.called) @mock.patch.object(os.path, 'isfile', lambda path: False) @mock.patch('ironic.common.utils.file_has_content', autospec=True) @@ -737,7 +781,19 @@ class TestPXEUtils(db_base.DbTestCase): self.config(tftp_server='192.0.2.1', group='pxe') elif ip_version == 6: self.config(tftp_server='ff80::1', group='pxe') - self.config(pxe_bootfile_name='fake-bootfile', group='pxe') + if CONF.deploy.default_boot_mode == 'uefi': + if ipxe: + self.config(uefi_ipxe_bootfile_name='fake-bootfile-ipxe', + group='pxe') + else: + self.config(uefi_pxe_bootfile_name='fake-bootfile', + group='pxe') + else: + if ipxe: + self.config(ipxe_bootfile_name='fake-bootfile-ipxe', + group='pxe') + else: + self.config(pxe_bootfile_name='fake-bootfile', group='pxe') self.config(tftp_root='/tftp-path/', group='pxe') if ipxe: bootfile = 'fake-bootfile-ipxe' @@ -774,12 +830,22 @@ class TestPXEUtils(db_base.DbTestCase): pxe_utils.dhcp_options_for_instance(task)) def test_dhcp_options_for_instance(self): + self.config(default_boot_mode='uefi', group='deploy') + self._dhcp_options_for_instance(ip_version=4) + + def test_dhcp_options_for_instance_bios(self): + self.config(default_boot_mode='bios', group='deploy') self._dhcp_options_for_instance(ip_version=4) def test_dhcp_options_for_instance_ipv6(self): self.config(tftp_server='ff80::1', group='pxe') self._dhcp_options_for_instance(ip_version=6) + def test_dhcp_options_for_instance_ipv6_bios(self): + self.config(tftp_server='ff80::1', group='pxe') + self.config(default_boot_mode='bios', group='deploy') + self._dhcp_options_for_instance(ip_version=6) + def _test_get_kernel_ramdisk_info(self, expected_dir, mode='deploy', ipxe_enabled=False): node_uuid = 'fake-node' @@ -1705,6 +1771,7 @@ class iPXEBuildConfigOptionsTestCase(db_base.DbTestCase): def test_dhcp_options_for_instance_ipxe_bios(self): self.config(ip_version=4, group='pxe') boot_file = 'fake-bootfile-bios-ipxe' + self.config(default_boot_mode='bios', group='deploy') self.config(ipxe_bootfile_name=boot_file, group='pxe') with task_manager.acquire(self.context, self.node.uuid) as task: self._dhcp_options_for_instance_ipxe(task, boot_file) @@ -1721,6 +1788,15 @@ class iPXEBuildConfigOptionsTestCase(db_base.DbTestCase): self.config(ip_version=6, group='pxe') boot_file = 'fake-bootfile-ipxe' self.config(ipxe_bootfile_name=boot_file, group='pxe') + self.config(default_boot_mode='bios', group='deploy') + with task_manager.acquire(self.context, self.node.uuid) as task: + self._dhcp_options_for_instance_ipxe(task, boot_file, ip_version=6) + + def test_dhcp_options_for_ipxe_ipv6_uefi(self): + self.config(ip_version=6, group='pxe') + boot_file = 'fake-bootfile-ipxe' + self.config(uefi_ipxe_bootfile_name=boot_file, group='pxe') + self.config(default_boot_mode='uefi', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: self._dhcp_options_for_instance_ipxe(task, boot_file, ip_version=6) diff --git a/ironic/tests/unit/drivers/modules/ansible/test_deploy.py b/ironic/tests/unit/drivers/modules/ansible/test_deploy.py index 886c01db5d..ed91995756 100644 --- a/ironic/tests/unit/drivers/modules/ansible/test_deploy.py +++ b/ironic/tests/unit/drivers/modules/ansible/test_deploy.py @@ -242,6 +242,7 @@ class TestAnsibleMethods(AnsibleDeployTestCaseBase): '--private-key=/path/to/key') def test__parse_partitioning_info_root_msdos(self): + self.config(default_boot_mode='bios', group='deploy') expected_info = { 'partition_info': { 'label': 'msdos', diff --git a/ironic/tests/unit/drivers/modules/redfish/test_management.py b/ironic/tests/unit/drivers/modules/redfish/test_management.py index 99da1265b4..88895ec167 100644 --- a/ironic/tests/unit/drivers/modules/redfish/test_management.py +++ b/ironic/tests/unit/drivers/modules/redfish/test_management.py @@ -290,7 +290,7 @@ class RedfishManagementTestCase(db_base.DbTestCase): fake_system.set_system_boot_options.assert_has_calls( [mock.call(sushy.BOOT_SOURCE_TARGET_PXE, enabled=expected), - mock.call(mode=sushy.BOOT_SOURCE_MODE_BIOS)]) + mock.call(mode=sushy.BOOT_SOURCE_MODE_UEFI)]) else: fake_system.set_system_boot_options.assert_has_calls( [mock.call(sushy.BOOT_SOURCE_TARGET_PXE, diff --git a/ironic/tests/unit/drivers/modules/test_agent_base.py b/ironic/tests/unit/drivers/modules/test_agent_base.py index c5a097c0fe..21c0b4fff2 100644 --- a/ironic/tests/unit/drivers/modules/test_agent_base.py +++ b/ironic/tests/unit/drivers/modules/test_agent_base.py @@ -982,6 +982,24 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): def test_configure_local_boot_whole_disk_image( self, install_bootloader_mock, try_set_boot_device_mock): + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + self.deploy.configure_local_boot(task) + # NOTE(TheJulia): We explicitly call install_bootloader when + # we have a whole disk image *and* are in UEFI mode as setting + # the internal NVRAM helps negate need to know a root device + # hint if the boot order is weird. + self.assertTrue(install_bootloader_mock.called) + try_set_boot_device_mock.assert_called_once_with( + task, boot_devices.DISK, persistent=True) + + @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) + @mock.patch.object(agent_client.AgentClient, 'install_bootloader', + autospec=True) + def test_configure_local_boot_whole_disk_image_bios( + self, install_bootloader_mock, try_set_boot_device_mock): + self.config(default_boot_mode='bios', group='deploy') + with task_manager.acquire(self.context, self.node['uuid'], shared=False) as task: self.deploy.configure_local_boot(task) @@ -1032,6 +1050,45 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): GlanceImageService_mock): image = GlanceImageService_mock.return_value.show.return_value image.get.return_value = {'rootfs_uuid': 'rootfs'} + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + task.node.driver_internal_info['is_whole_disk_image'] = True + task.node.target_raid_config = { + "logical_disks": [ + { + "size_gb": 100, + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": 'MAX', + "raid_level": "0", + "controller": "software", + } + ] + } + self.deploy.configure_local_boot(task) + self.assertTrue(GlanceImageService_mock.called) + install_bootloader_mock.assert_called_once_with( + mock.ANY, task.node, + root_uuid='rootfs', + efi_system_part_uuid=None, + prep_boot_part_uuid=None, + target_boot_mode='uefi', + software_raid=True) + try_set_boot_device_mock.assert_called_once_with( + task, boot_devices.DISK, persistent=True) + + @mock.patch.object(image_service, 'GlanceImageService', autospec=True) + @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) + @mock.patch.object(agent_client.AgentClient, 'install_bootloader', + autospec=True) + def test_configure_local_boot_on_software_raid_bios( + self, install_bootloader_mock, try_set_boot_device_mock, + GlanceImageService_mock): + self.config(default_boot_mode='bios', group='deploy') + image = GlanceImageService_mock.return_value.show.return_value + image.get.return_value = {'rootfs_uuid': 'rootfs'} with task_manager.acquire(self.context, self.node['uuid'], shared=False) as task: task.node.driver_internal_info['is_whole_disk_image'] = True @@ -1068,6 +1125,44 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): def test_configure_local_boot_on_software_raid_explicit_uuid( self, install_bootloader_mock, try_set_boot_device_mock, GlanceImageService_mock): + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + task.node.driver_internal_info['is_whole_disk_image'] = True + task.node.instance_info['image_rootfs_uuid'] = 'rootfs' + task.node.target_raid_config = { + "logical_disks": [ + { + "size_gb": 100, + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": 'MAX', + "raid_level": "0", + "controller": "software", + } + ] + } + self.deploy.configure_local_boot(task) + self.assertFalse(GlanceImageService_mock.called) + install_bootloader_mock.assert_called_once_with( + mock.ANY, task.node, + root_uuid='rootfs', + efi_system_part_uuid=None, + prep_boot_part_uuid=None, + target_boot_mode='uefi', + software_raid=True) + try_set_boot_device_mock.assert_called_once_with( + task, boot_devices.DISK, persistent=True) + + @mock.patch.object(image_service, 'GlanceImageService', autospec=True) + @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) + @mock.patch.object(agent_client.AgentClient, 'install_bootloader', + autospec=True) + def test_configure_local_boot_on_software_raid_explicit_uuid_bios( + self, install_bootloader_mock, try_set_boot_device_mock, + GlanceImageService_mock): + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node['uuid'], shared=False) as task: task.node.driver_internal_info['is_whole_disk_image'] = True @@ -1102,10 +1197,48 @@ class AgentDeployMixinTest(AgentDeployMixinBaseTest): @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) @mock.patch.object(agent_client.AgentClient, 'install_bootloader', autospec=True) - def test_configure_local_boot_on_software_raid_exception( + def test_configure_local_boot_on_software_raid_exception_uefi( self, install_bootloader_mock, try_set_boot_device_mock, GlanceImageService_mock): GlanceImageService_mock.side_effect = Exception('Glance not found') + with task_manager.acquire(self.context, self.node['uuid'], + shared=False) as task: + task.node.driver_internal_info['is_whole_disk_image'] = True + root_uuid = "1efecf88-2b58-4d4e-8fbd-7bef1a40a1b0" + task.node.driver_internal_info['root_uuid_or_disk_id'] = root_uuid + task.node.target_raid_config = { + "logical_disks": [ + { + "size_gb": 100, + "raid_level": "1", + "controller": "software", + }, + { + "size_gb": 'MAX', + "raid_level": "0", + "controller": "software", + } + ] + } + self.deploy.configure_local_boot(task) + self.assertTrue(GlanceImageService_mock.called) + # check if the root_uuid comes from the driver_internal_info + install_bootloader_mock.assert_called_once_with( + mock.ANY, task.node, root_uuid=root_uuid, + efi_system_part_uuid=None, prep_boot_part_uuid=None, + target_boot_mode='uefi', software_raid=True) + try_set_boot_device_mock.assert_called_once_with( + task, boot_devices.DISK, persistent=True) + + @mock.patch.object(image_service, 'GlanceImageService', autospec=True) + @mock.patch.object(deploy_utils, 'try_set_boot_device', autospec=True) + @mock.patch.object(agent_client.AgentClient, 'install_bootloader', + autospec=True) + def test_configure_local_boot_on_software_raid_exception_bios( + self, install_bootloader_mock, try_set_boot_device_mock, + GlanceImageService_mock): + self.config(default_boot_mode='bios', group='deploy') + GlanceImageService_mock.side_effect = Exception('Glance not found') with task_manager.acquire(self.context, self.node['uuid'], shared=False) as task: task.node.driver_internal_info['is_whole_disk_image'] = True diff --git a/ironic/tests/unit/drivers/modules/test_boot_mode_utils.py b/ironic/tests/unit/drivers/modules/test_boot_mode_utils.py index 611bc0ec45..646d307895 100644 --- a/ironic/tests/unit/drivers/modules/test_boot_mode_utils.py +++ b/ironic/tests/unit/drivers/modules/test_boot_mode_utils.py @@ -50,13 +50,26 @@ class GetBootModeTestCase(tests_base.TestCase): @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', autospec=True) def test_get_boot_mode_default(self, mock_for_deploy, mock_log): + boot_mode_utils.warn_about_default_boot_mode = False + mock_for_deploy.return_value = None + boot_mode = boot_mode_utils.get_boot_mode(self.node) + self.assertEqual(boot_modes.UEFI, boot_mode) + boot_mode = boot_mode_utils.get_boot_mode(self.node) + self.assertEqual(boot_modes.UEFI, boot_mode) + self.assertEqual(0, mock_log.warning.call_count) + + @mock.patch.object(boot_mode_utils, 'LOG', autospec=True) + @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', + autospec=True) + def test_get_boot_mode_bios_default(self, mock_for_deploy, mock_log): + self.config(default_boot_mode='bios', group='deploy') boot_mode_utils.warn_about_default_boot_mode = False mock_for_deploy.return_value = None boot_mode = boot_mode_utils.get_boot_mode(self.node) self.assertEqual(boot_modes.LEGACY_BIOS, boot_mode) boot_mode = boot_mode_utils.get_boot_mode(self.node) self.assertEqual(boot_modes.LEGACY_BIOS, boot_mode) - self.assertEqual(1, mock_log.warning.call_count) + self.assertEqual(0, mock_log.warning.call_count) @mock.patch.object(boot_mode_utils, 'LOG', autospec=True) @mock.patch.object(boot_mode_utils, 'get_boot_mode_for_deploy', diff --git a/ironic/tests/unit/drivers/modules/test_deploy_utils.py b/ironic/tests/unit/drivers/modules/test_deploy_utils.py index 4021558bf2..cf6c8484ea 100644 --- a/ironic/tests/unit/drivers/modules/test_deploy_utils.py +++ b/ironic/tests/unit/drivers/modules/test_deploy_utils.py @@ -575,13 +575,27 @@ class GetPxeBootConfigTestCase(db_base.DbTestCase): self.assertEqual('aarch64-template', result) def test_get_pxe_boot_file_emtpy_property(self): + self.node.properties = {} + self.config(pxe_bootfile_name_by_arch=self.bootfile_by_arch, + group='pxe') + result = utils.get_pxe_boot_file(self.node) + self.assertEqual('uefi-bootfile', result) + + def test_get_pxe_boot_file_emtpy_property_bios_default(self): + self.config(default_boot_mode='bios', group='deploy') self.node.properties = {} self.config(pxe_bootfile_name_by_arch=self.bootfile_by_arch, group='pxe') result = utils.get_pxe_boot_file(self.node) self.assertEqual('bios-bootfile', result) - def test_get_ipxe_boot_file(self): + def test_get_ipxe_boot_uefi(self): + self.config(uefi_ipxe_bootfile_name='meow', group='pxe') + result = utils.get_ipxe_boot_file(self.node) + self.assertEqual('meow', result) + + def test_get_ipxe_boot_bios(self): + self.config(default_boot_mode='bios', group='deploy') self.config(ipxe_bootfile_name='meow', group='pxe') result = utils.get_ipxe_boot_file(self.node) self.assertEqual('meow', result) @@ -603,6 +617,15 @@ class GetPxeBootConfigTestCase(db_base.DbTestCase): self.assertEqual('ipxe-aa64.efi', result) def test_get_ipxe_boot_file_fallback(self): + self.config(ipxe_bootfile_name=None, group='pxe') + self.config(uefi_ipxe_bootfile_name=None, group='pxe') + self.config(pxe_bootfile_name=None, group='pxe') + self.config(uefi_pxe_bootfile_name='lolcat', group='pxe') + result = utils.get_ipxe_boot_file(self.node) + self.assertEqual('lolcat', result) + + def test_get_ipxe_boot_file_fallback_bios(self): + self.config(default_boot_mode='bios', group='deploy') self.config(ipxe_bootfile_name=None, group='pxe') self.config(uefi_ipxe_bootfile_name=None, group='pxe') self.config(pxe_bootfile_name='lolcat', group='pxe') @@ -610,6 +633,14 @@ class GetPxeBootConfigTestCase(db_base.DbTestCase): self.assertEqual('lolcat', result) def test_get_pxe_config_template_emtpy_property(self): + self.node.properties = {} + self.config(pxe_config_template_by_arch=self.template_by_arch, + group='pxe') + result = utils.get_pxe_config_template(self.node) + self.assertEqual('uefi-template', result) + + def test_get_pxe_config_template_emtpy_property_bios(self): + self.config(default_boot_mode='bios', group='deploy') self.node.properties = {} self.config(pxe_config_template_by_arch=self.template_by_arch, group='pxe') @@ -631,6 +662,16 @@ class GetPxeBootConfigTestCase(db_base.DbTestCase): utils.get_ipxe_config_template(node)) def test_get_ipxe_config_template_none(self): + self.config(ipxe_config_template=None, group='pxe') + self.config(uefi_pxe_config_template='magical_bootloader', + group='pxe') + node = obj_utils.create_test_node( + self.context, driver='fake-hardware') + self.assertEqual('magical_bootloader', + utils.get_ipxe_config_template(node)) + + def test_get_ipxe_config_template_none_bios(self): + self.config(default_boot_mode='bios', group='deploy') self.config(ipxe_config_template=None, group='pxe') self.config(pxe_config_template='magical_bootloader', group='pxe') @@ -1010,6 +1051,13 @@ class ParseInstanceInfoCapabilitiesTestCase(tests_base.TestCase): self.assertEqual('gpt', result) def test_get_disk_label_nothing_set(self): + inst_info = {'capabilities': {'cat': 'meows'}} + self.node.instance_info = inst_info + result = utils.get_disk_label(self.node) + self.assertEqual('gpt', result) + + def test_get_disk_label_nothing_set_bios_mode(self): + self.config(default_boot_mode='bios', group='deploy') inst_info = {'capabilities': {'cat': 'meows'}} self.node.instance_info = inst_info result = utils.get_disk_label(self.node) @@ -1058,6 +1106,7 @@ class TrySetBootDeviceTestCase(db_base.DbTestCase): @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) def test_try_set_boot_device_ipmifailure_bios( self, node_set_boot_device_mock): + self.config(default_boot_mode='bios', group='deploy') node_set_boot_device_mock.side_effect = exception.IPMIFailure(cmd='a') with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py index ff061277a9..58a8061727 100644 --- a/ironic/tests/unit/drivers/modules/test_image_utils.py +++ b/ironic/tests/unit/drivers/modules/test_image_utils.py @@ -509,6 +509,7 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_bios( self, mock_create_boot_iso, mock_publish_image): + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: @@ -551,7 +552,7 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): mock_create_boot_iso.assert_called_once_with( mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', - boot_mode='bios', esp_image_href=None, + boot_mode='uefi', esp_image_href=None, kernel_params=kernel_params, root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', inject_files=None) @@ -567,6 +568,29 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): task.node.driver_info['kernel_append_params'] = kernel_params + image_utils._prepare_iso_image( + task, 'http://kernel/img', 'http://ramdisk/img', + bootloader_href=None, root_uuid=task.node.uuid) + + mock_create_boot_iso.assert_called_once_with( + mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', + boot_mode='uefi', esp_image_href=None, + kernel_params=kernel_params, + root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', + inject_files=None) + + @mock.patch.object(image_utils.ImageHandler, 'publish_image', + autospec=True) + @mock.patch.object(images, 'create_boot_iso', autospec=True) + def test__prepare_iso_image_kernel_params_driver_info_bios( + self, mock_create_boot_iso, mock_publish_image): + self.config(default_boot_mode='bios', group='deploy') + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + kernel_params = 'network-config=base64-cloudinit-blob' + + task.node.driver_info['kernel_append_params'] = kernel_params + image_utils._prepare_iso_image( task, 'http://kernel/img', 'http://ramdisk/img', bootloader_href=None, root_uuid=task.node.uuid) @@ -582,7 +606,7 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) - def test__prepare_iso_image_kernel_params_for_ramdisk( + def test__prepare_iso_image_kernel_params_for_ramdisk_uefi( self, mock_create_boot_iso, mock_publish_image): with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: @@ -590,6 +614,30 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): task.node.instance_info['ramdisk_kernel_arguments'] = kernel_params + image_utils._prepare_iso_image( + task, 'http://kernel/img', 'http://ramdisk/img', + bootloader_href=None, root_uuid=task.node.uuid) + + mock_create_boot_iso.assert_called_once_with( + mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', + boot_mode='uefi', esp_image_href=None, + kernel_params="root=/dev/ram0 text " + kernel_params, + root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', + inject_files=None) + + @mock.patch.object(deploy_utils, 'get_boot_option', lambda node: 'ramdisk') + @mock.patch.object(image_utils.ImageHandler, 'publish_image', + autospec=True) + @mock.patch.object(images, 'create_boot_iso', autospec=True) + def test__prepare_iso_image_kernel_params_for_ramdisk_bios( + self, mock_create_boot_iso, mock_publish_image): + self.config(default_boot_mode='bios', group='deploy') + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + kernel_params = 'network-config=base64-cloudinit-blob' + + task.node.instance_info['ramdisk_kernel_arguments'] = kernel_params + image_utils._prepare_iso_image( task, 'http://kernel/img', 'http://ramdisk/img', bootloader_href=None, root_uuid=task.node.uuid) @@ -620,7 +668,7 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): mock_create_boot_iso.assert_called_once_with( mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', - boot_mode='bios', esp_image_href=None, + boot_mode='uefi', esp_image_href=None, kernel_params=kernel_params, root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', inject_files=None) @@ -643,7 +691,7 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): mock_create_boot_iso.assert_called_once_with( mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', - boot_mode='bios', esp_image_href=None, + boot_mode='uefi', esp_image_href=None, kernel_params=kernel_params + ' foo=bar banana', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', inject_files=None) diff --git a/ironic/tests/unit/drivers/modules/test_ipmitool.py b/ironic/tests/unit/drivers/modules/test_ipmitool.py index 1c9e61bf97..2507b431f0 100644 --- a/ironic/tests/unit/drivers/modules/test_ipmitool.py +++ b/ironic/tests/unit/drivers/modules/test_ipmitool.py @@ -2446,9 +2446,9 @@ class IPMIToolDriverTestCase(Base): self.assertEqual({}, driver_routes) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_set_boot_device_ok(self, mock_exec): + def test_management_interface_set_boot_device_ok_bios(self, mock_exec): mock_exec.return_value = [None, None] - + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: self.management.set_boot_device(task, boot_devices.PXE) @@ -2458,10 +2458,10 @@ class IPMIToolDriverTestCase(Base): @mock.patch.object(driver_utils, 'force_persistent_boot', autospec=True) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_no_force_set_boot_device(self, - mock_exec, - mock_force_boot): + def test_management_interface_no_force_set_boot_device_bios( + self, mock_exec, mock_force_boot): mock_exec.return_value = [None, None] + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: driver_info = task.node.driver_info @@ -2476,8 +2476,10 @@ class IPMIToolDriverTestCase(Base): self.assertFalse(mock_force_boot.called) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_force_set_boot_device_ok(self, mock_exec): + def test_management_interface_force_set_boot_device_ok_bios(self, + mock_exec): mock_exec.return_value = [None, None] + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: driver_info = task.node.driver_info @@ -2496,8 +2498,10 @@ class IPMIToolDriverTestCase(Base): mock_exec.assert_has_calls(mock_calls) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_set_boot_device_persistent(self, mock_exec): + def test_management_interface_set_boot_device_persistent_bios(self, + mock_exec): mock_exec.return_value = [None, None] + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: driver_info = task.node.driver_info @@ -2520,10 +2524,24 @@ class IPMIToolDriverTestCase(Base): task, 'fake-device') @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_set_boot_device_without_timeout_1(self, - mock_exec): + def test_management_interface_set_boot_device_without_timeout_1_uefi( + self, mock_exec): mock_exec.return_value = [None, None] + with task_manager.acquire(self.context, self.node.uuid) as task: + driver_info = task.node.driver_info + driver_info['ipmi_disable_boot_timeout'] = 'False' + task.node.driver_info = driver_info + self.management.set_boot_device(task, boot_devices.PXE) + mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x05 0xa0 0x04 " + "0x00 0x00 0x00")] + mock_exec.assert_has_calls(mock_calls) + + @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) + def test_management_interface_set_boot_device_without_timeout_1_bios( + self, mock_exec): + mock_exec.return_value = [None, None] + self.config(default_boot_mode='bios', group='deploy') with task_manager.acquire(self.context, self.node.uuid) as task: driver_info = task.node.driver_info driver_info['ipmi_disable_boot_timeout'] = 'False' @@ -2534,11 +2552,25 @@ class IPMIToolDriverTestCase(Base): mock_exec.assert_has_calls(mock_calls) @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) - def test_management_interface_set_boot_device_without_timeout_2(self, - mock_exec): + def test_management_interface_set_boot_device_without_timeout_2_uefi( + self, mock_exec): CONF.set_override('disable_boot_timeout', False, 'ipmi') mock_exec.return_value = [None, None] + with task_manager.acquire(self.context, self.node.uuid) as task: + self.management.set_boot_device(task, boot_devices.PXE) + + mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x05 0xa0 0x04 " + "0x00 0x00 0x00")] + mock_exec.assert_has_calls(mock_calls) + + @mock.patch.object(ipmi, '_exec_ipmitool', autospec=True) + def test_management_interface_set_boot_device_without_timeout_2_bios( + self, mock_exec): + CONF.set_override('disable_boot_timeout', False, 'ipmi') + self.config(default_boot_mode='bios', group='deploy') + mock_exec.return_value = [None, None] + with task_manager.acquire(self.context, self.node.uuid) as task: self.management.set_boot_device(task, boot_devices.PXE) diff --git a/ironic/tests/unit/drivers/modules/test_ipxe.py b/ironic/tests/unit/drivers/modules/test_ipxe.py index 32bc1f3ede..45a68eeb37 100644 --- a/ironic/tests/unit/drivers/modules/test_ipxe.py +++ b/ironic/tests/unit/drivers/modules/test_ipxe.py @@ -606,12 +606,57 @@ class iPXEBootTestCase(db_base.DbTestCase): task, ipxe_enabled=True, ip_version=6) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid, ipxe_enabled=True) + task.node.properties['capabilities'] = 'boot_mode:uefi' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['root_uuid_or_disk_id'] = ( + "30212642-09d3-467f-8e09-21685826ab50") + task.node.driver_internal_info['is_whole_disk_image'] = False + + task.driver.boot.prepare_instance(task) + + get_image_info_mock.assert_called_once_with( + task, ipxe_enabled=True) + cache_mock.assert_called_once_with(task, image_info, + ipxe_enabled=True) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50", + 'uefi', False, False, False, False, ipxe_enabled=True, + anaconda_boot=False) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_netboot_bios( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock): + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + image_info = {'kernel': ('', '/path/to/kernel'), + 'ramdisk': ('', '/path/to/ramdisk')} + instance_info = {"boot_option": "netboot", + "boot_mode": "bios"} + get_image_info_mock.return_value = image_info + with task_manager.acquire(self.context, self.node.uuid) as task: task.node.properties['capabilities'] = 'boot_mode:bios' task.node.instance_info['capabilities'] = instance_info task.node.driver_internal_info['root_uuid_or_disk_id'] = ( "30212642-09d3-467f-8e09-21685826ab50") task.node.driver_internal_info['is_whole_disk_image'] = False + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid, ipxe_enabled=True) + task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( @@ -641,23 +686,25 @@ class iPXEBootTestCase(db_base.DbTestCase): dhcp_factory_mock.return_value = provider_mock image_info = {'kernel': ('', '/path/to/kernel'), 'ramdisk': ('', '/path/to/ramdisk')} - i_info_caps = {"boot_option": "ramdisk"} + i_info_caps = {"boot_option": "ramdisk", + "boot_mode": "bios"} kernel_arg = "meow" get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: - dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=True) - dhcp_opts += pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=True, ip_version=6) - pxe_config_path = pxe_utils.get_pxe_config_file_path( - task.node.uuid, ipxe_enabled=True) task.node.properties['capabilities'] = 'boot_mode:bios' i_info = task.node.instance_info i_info['capabilities'] = i_info_caps i_info['kernel_append_params'] = kernel_arg task.node.instance_info = i_info task.node.save() + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid, ipxe_enabled=True) + task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( task, ipxe_enabled=True) @@ -681,6 +728,60 @@ class iPXEBootTestCase(db_base.DbTestCase): mock_create_pxe_config.assert_called_once_with( task, expected_params, mock.ANY, ipxe_enabled=True) + @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_ramdisk_bios( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock, mock_create_pxe_config): + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + image_info = {'kernel': ('', '/path/to/kernel'), + 'ramdisk': ('', '/path/to/ramdisk')} + i_info_caps = {"boot_option": "ramdisk"} + kernel_arg = "meow" + get_image_info_mock.return_value = image_info + + with task_manager.acquire(self.context, self.node.uuid) as task: + i_info = task.node.instance_info + i_info['capabilities'] = i_info_caps + i_info['kernel_append_params'] = kernel_arg + task.node.instance_info = i_info + task.node.save() + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid, ipxe_enabled=True) + + task.driver.boot.prepare_instance(task) + get_image_info_mock.assert_called_once_with( + task, ipxe_enabled=True) + cache_mock.assert_called_once_with(task, image_info, + ipxe_enabled=True) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, None, + 'uefi', False, iscsi_boot=False, ramdisk_boot=True, + ipxe_enabled=True, anaconda_boot=False) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + expected_params = { + 'aki_path': 'http://myserver/' + task.node.uuid + '/kernel', + 'ari_path': 'http://myserver/' + task.node.uuid + '/ramdisk', + 'pxe_append_params': 'meow ipa-debug=1 ipa-global-request-id' + '=' + task.context.request_id, + 'tftp_server': mock.ANY, + 'ipxe_timeout': 0} + mock_create_pxe_config.assert_called_once_with( + task, expected_params, mock.ANY, ipxe_enabled=True) + @mock.patch('os.path.isfile', return_value=False, autospec=True) @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) @@ -701,17 +802,18 @@ class iPXEBootTestCase(db_base.DbTestCase): self.node.provision_state = states.ACTIVE self.node.save() with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['root_uuid_or_disk_id'] = ( + "30212642-09d3-467f-8e09-21685826ab50") + task.node.driver_internal_info['is_whole_disk_image'] = False + dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True, ip_version=6) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid, ipxe_enabled=True) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.instance_info['capabilities'] = instance_info - task.node.driver_internal_info['root_uuid_or_disk_id'] = ( - "30212642-09d3-467f-8e09-21685826ab50") - task.node.driver_internal_info['is_whole_disk_image'] = False task.driver.boot.prepare_instance(task) @@ -745,13 +847,48 @@ class iPXEBootTestCase(db_base.DbTestCase): get_image_info_mock.return_value = image_info instance_info = {"boot_option": "netboot"} with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['is_whole_disk_image'] = False dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True, ip_version=4) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True, ip_version=6) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.instance_info['capabilities'] = instance_info + + task.driver.boot.prepare_instance(task) + + get_image_info_mock.assert_called_once_with( + task, ipxe_enabled=True) + cache_mock.assert_called_once_with(task, image_info, + ipxe_enabled=True) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + self.assertFalse(switch_pxe_config_mock.called) + self.assertFalse(set_boot_device_mock.called) + + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_netboot_missing_root_uuid_default( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock): + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + image_info = {'kernel': ('', '/path/to/kernel'), + 'ramdisk': ('', '/path/to/ramdisk')} + get_image_info_mock.return_value = image_info + instance_info = self.node.instance_info + instance_info['capabilities'] = {"boot_option": "netboot"} + self.node.instance_info = instance_info + self.node.save() + with task_manager.acquire(self.context, self.node.uuid) as task: task.node.driver_internal_info['is_whole_disk_image'] = False + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=4) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) task.driver.boot.prepare_instance(task) @@ -778,15 +915,17 @@ class iPXEBootTestCase(db_base.DbTestCase): provider_mock = mock.MagicMock() dhcp_factory_mock.return_value = provider_mock get_image_info_mock.return_value = {} - instance_info = {"boot_option": "netboot"} + instance_info = {"boot_option": "netboot", + "boot_mode": "bios"} with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['is_whole_disk_image'] = True dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=True, ip_version=6) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.instance_info['capabilities'] = instance_info - task.node.driver_internal_info['is_whole_disk_image'] = True + task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( task, ipxe_enabled=True) @@ -797,6 +936,60 @@ class iPXEBootTestCase(db_base.DbTestCase): set_boot_device_mock.assert_called_once_with( task, boot_devices.DISK, persistent=True) + @mock.patch('os.path.isfile', lambda filename: False) + @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) + @mock.patch.object(deploy_utils, 'is_iscsi_boot', lambda task: True) + @mock.patch.object(noop_storage.NoopStorage, 'should_write_image', + lambda task: False) + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_netboot_iscsi_bios( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock, create_pxe_config_mock): + http_url = 'http://192.1.2.3:1234' + self.config(http_url=http_url, group='deploy') + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + vol_id = uuidutils.generate_uuid() + obj_utils.create_test_volume_target( + self.context, node_id=self.node.id, volume_type='iscsi', + boot_index=0, volume_id='1234', uuid=vol_id, + properties={'target_lun': 0, + 'target_portal': 'fake_host:3260', + 'target_iqn': 'fake_iqn', + 'auth_username': 'fake_username', + 'auth_password': 'fake_password'}) + with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.driver_internal_info = { + 'boot_from_volume': vol_id} + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = {'boot_mode': 'bios'} + dhcp_opts = pxe_utils.dhcp_options_for_instance(task, + ipxe_enabled=True) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=True, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid, ipxe_enabled=True) + + task.driver.boot.prepare_instance(task) + self.assertFalse(get_image_info_mock.called) + self.assertFalse(cache_mock.called) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + create_pxe_config_mock.assert_called_once_with( + task, mock.ANY, CONF.pxe.ipxe_config_template, + ipxe_enabled=True) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, None, boot_modes.LEGACY_BIOS, False, + ipxe_enabled=True, iscsi_boot=True, ramdisk_boot=False, + anaconda_boot=False) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + @mock.patch('os.path.isfile', lambda filename: False) @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) @mock.patch.object(deploy_utils, 'is_iscsi_boot', lambda task: True) @@ -824,6 +1017,7 @@ class iPXEBootTestCase(db_base.DbTestCase): 'target_iqn': 'fake_iqn', 'auth_username': 'fake_username', 'auth_password': 'fake_password'}) + with task_manager.acquire(self.context, self.node.uuid) as task: task.node.driver_internal_info = { 'boot_from_volume': vol_id} @@ -833,7 +1027,7 @@ class iPXEBootTestCase(db_base.DbTestCase): task, ipxe_enabled=True, ip_version=6) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid, ipxe_enabled=True) - task.node.properties['capabilities'] = 'boot_mode:bios' + task.driver.boot.prepare_instance(task) self.assertFalse(get_image_info_mock.called) self.assertFalse(cache_mock.called) @@ -842,7 +1036,7 @@ class iPXEBootTestCase(db_base.DbTestCase): task, mock.ANY, CONF.pxe.ipxe_config_template, ipxe_enabled=True) switch_pxe_config_mock.assert_called_once_with( - pxe_config_path, None, boot_modes.LEGACY_BIOS, False, + pxe_config_path, None, boot_modes.UEFI, False, ipxe_enabled=True, iscsi_boot=True, ramdisk_boot=False, anaconda_boot=False) set_boot_device_mock.assert_called_once_with(task, @@ -874,7 +1068,6 @@ class iPXEBootTestCase(db_base.DbTestCase): self.node.provision_state = states.DEPLOYING self.node.save() with task_manager.acquire(self.context, self.node.uuid) as task: - print(task.node) dhcp_opts = pxe_utils.dhcp_options_for_instance(task, ipxe_enabled=True) dhcp_opts += pxe_utils.dhcp_options_for_instance( @@ -889,7 +1082,7 @@ class iPXEBootTestCase(db_base.DbTestCase): task, mock.ANY, CONF.pxe.ipxe_config_template, ipxe_enabled=True) switch_pxe_config_mock.assert_called_once_with( - pxe_config_path, None, boot_modes.LEGACY_BIOS, False, + pxe_config_path, None, boot_modes.UEFI, False, ipxe_enabled=True, iscsi_boot=False, ramdisk_boot=True, anaconda_boot=False) set_boot_device_mock.assert_called_once_with(task, @@ -959,7 +1152,7 @@ class iPXEBootTestCase(db_base.DbTestCase): persistent=True) switch_pxe_config_mock.assert_called_once_with( pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50", - 'bios', True, False, False, False, ipxe_enabled=True, + 'uefi', True, False, False, False, ipxe_enabled=True, anaconda_boot=False) # No clean up self.assertFalse(clean_up_pxe_config_mock.called) diff --git a/ironic/tests/unit/drivers/modules/test_pxe.py b/ironic/tests/unit/drivers/modules/test_pxe.py index 8847b9813d..5b8940e293 100644 --- a/ironic/tests/unit/drivers/modules/test_pxe.py +++ b/ironic/tests/unit/drivers/modules/test_pxe.py @@ -271,7 +271,7 @@ class PXEBootTestCase(db_base.DbTestCase): dhcp_factory_mock, set_boot_device_mock, get_boot_mode_mock, - uefi=False, + uefi=True, cleaning=False, ipxe_use_swift=False, whole_disk_image=False, @@ -295,6 +295,7 @@ class PXEBootTestCase(db_base.DbTestCase): driver_internal_info = self.node.driver_internal_info driver_internal_info['is_whole_disk_image'] = whole_disk_image self.node.driver_internal_info = driver_internal_info + if mode == 'rescue': mock_deploy_img_info.return_value = { 'rescue_kernel': 'a', @@ -356,11 +357,21 @@ class PXEBootTestCase(db_base.DbTestCase): self.node.save() self._test_prepare_ramdisk() + def test_prepare_ramdisk_bios(self): + self.node.provision_state = states.DEPLOYING + self.node.save() + self._test_prepare_ramdisk(uefi=True) + def test_prepare_ramdisk_rescue(self): self.node.provision_state = states.RESCUING self.node.save() self._test_prepare_ramdisk(mode='rescue') + def test_prepare_ramdisk_rescue_bios(self): + self.node.provision_state = states.RESCUING + self.node.save() + self._test_prepare_ramdisk(mode='rescue', uefi=True) + def test_prepare_ramdisk_uefi(self): self.node.provision_state = states.DEPLOYING self.node.save() @@ -391,7 +402,8 @@ class PXEBootTestCase(db_base.DbTestCase): self, set_boot_mode_mock): self.node.provision_state = states.DEPLOYING self.node.save() - self._test_prepare_ramdisk(node_boot_mode=boot_modes.LEGACY_BIOS) + self._test_prepare_ramdisk(node_boot_mode=boot_modes.LEGACY_BIOS, + uefi=False) with task_manager.acquire(self.context, self.node.uuid) as task: driver_internal_info = task.node.driver_internal_info @@ -408,8 +420,7 @@ class PXEBootTestCase(db_base.DbTestCase): self.config(default_boot_mode=boot_modes.LEGACY_BIOS, group='deploy') - self._test_prepare_ramdisk() - + self._test_prepare_ramdisk(uefi=False) with task_manager.acquire(self.context, self.node.uuid) as task: driver_internal_info = task.node.driver_internal_info self.assertIn('deploy_boot_mode', driver_internal_info) @@ -470,7 +481,7 @@ class PXEBootTestCase(db_base.DbTestCase): properties['capabilities'] = 'boot_mode:uefi' self.node.properties = properties self.node.save() - self._test_prepare_ramdisk(uefi=True, node_boot_mode=boot_modes.UEFI) + self._test_prepare_ramdisk(node_boot_mode=boot_modes.UEFI) self.assertEqual(set_boot_mode_mock.call_count, 0) @mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True) @@ -504,7 +515,7 @@ class PXEBootTestCase(db_base.DbTestCase): @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) - def test_prepare_instance_netboot( + def test_prepare_instance_netboot_bios( self, get_image_info_mock, cache_mock, dhcp_factory_mock, switch_pxe_config_mock, set_boot_device_mock): @@ -514,18 +525,19 @@ class PXEBootTestCase(db_base.DbTestCase): 'ramdisk': ('', '/path/to/ramdisk')} get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.driver_internal_info['root_uuid_or_disk_id'] = ( + "30212642-09d3-467f-8e09-21685826ab50") + task.node.driver_internal_info['is_whole_disk_image'] = False + task.node.instance_info = { + 'capabilities': {'boot_option': 'netboot', + 'boot_mode': 'bios'}} dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False, ip_version=4) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False, ip_version=6) pxe_config_path = pxe_utils.get_pxe_config_file_path( task.node.uuid) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.driver_internal_info['root_uuid_or_disk_id'] = ( - "30212642-09d3-467f-8e09-21685826ab50") - task.node.driver_internal_info['is_whole_disk_image'] = False - task.node.instance_info = { - 'capabilities': {'boot_option': 'netboot'}} task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with( @@ -541,6 +553,47 @@ class PXEBootTestCase(db_base.DbTestCase): boot_devices.PXE, persistent=True) + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + def test_prepare_instance_netboot_uefi( + self, get_image_info_mock, cache_mock, + dhcp_factory_mock, switch_pxe_config_mock, + set_boot_device_mock): + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + image_info = {'kernel': ('', '/path/to/kernel'), + 'ramdisk': ('', '/path/to/ramdisk')} + get_image_info_mock.return_value = image_info + with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.driver_internal_info['root_uuid_or_disk_id'] = ( + "30212642-09d3-467f-8e09-21685826ab50") + task.node.driver_internal_info['is_whole_disk_image'] = False + task.node.instance_info = { + 'capabilities': {'boot_option': 'netboot'}} + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False, ip_version=4) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid) + task.driver.boot.prepare_instance(task) + + get_image_info_mock.assert_called_once_with( + task, ipxe_enabled=False) + cache_mock.assert_called_once_with( + task, image_info, ipxe_enabled=False) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50", + 'uefi', False, False, False, False, ipxe_enabled=False, + anaconda_boot=False) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + @mock.patch('os.path.isfile', return_value=False, autospec=True) @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) @@ -561,18 +614,18 @@ class PXEBootTestCase(db_base.DbTestCase): self.node.provision_state = states.ACTIVE self.node.save() with task_manager.acquire(self.context, self.node.uuid) as task: - dhcp_opts = pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=False) - dhcp_opts += pxe_utils.dhcp_options_for_instance( - task, ipxe_enabled=False, ip_version=6) - pxe_config_path = pxe_utils.get_pxe_config_file_path( - task.node.uuid) task.node.properties['capabilities'] = 'boot_mode:bios' task.node.driver_internal_info['root_uuid_or_disk_id'] = ( "30212642-09d3-467f-8e09-21685826ab50") task.node.driver_internal_info['is_whole_disk_image'] = False task.node.instance_info['capabilities'] = instance_info task.driver.boot.prepare_instance(task) + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid) get_image_info_mock.assert_called_once_with( task, ipxe_enabled=False) @@ -604,13 +657,13 @@ class PXEBootTestCase(db_base.DbTestCase): instance_info = {"boot_option": "netboot"} get_image_info_mock.return_value = image_info with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['is_whole_disk_image'] = False dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False, ip_version=6) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.instance_info['capabilities'] = instance_info - task.node.driver_internal_info['is_whole_disk_image'] = False task.driver.boot.prepare_instance(task) @@ -637,13 +690,13 @@ class PXEBootTestCase(db_base.DbTestCase): get_image_info_mock.return_value = {} instance_info = {"boot_option": "netboot"} with task_manager.acquire(self.context, self.node.uuid) as task: + task.node.properties['capabilities'] = 'boot_mode:bios' + task.node.instance_info['capabilities'] = instance_info + task.node.driver_internal_info['is_whole_disk_image'] = True dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False) dhcp_opts += pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False, ip_version=6) - task.node.properties['capabilities'] = 'boot_mode:bios' - task.node.instance_info['capabilities'] = instance_info - task.node.driver_internal_info['is_whole_disk_image'] = True task.driver.boot.prepare_instance(task) get_image_info_mock.assert_called_once_with(task, ipxe_enabled=False) @@ -702,7 +755,8 @@ class PXEBootTestCase(db_base.DbTestCase): self, get_image_info_mock, cache_mock, dhcp_factory_mock, create_pxe_config_mock, switch_pxe_config_mock, - set_boot_device_mock, config_file_exits=False): + set_boot_device_mock, config_file_exits=False, + uefi=True): image_info = {'kernel': ['', '/path/to/kernel'], 'ramdisk': ['', '/path/to/ramdisk']} get_image_info_mock.return_value = image_info @@ -731,12 +785,22 @@ class PXEBootTestCase(db_base.DbTestCase): if config_file_exits: self.assertFalse(create_pxe_config_mock.called) else: - create_pxe_config_mock.assert_called_once_with( - task, mock.ANY, CONF.pxe.pxe_config_template, - ipxe_enabled=False) + if not uefi: + create_pxe_config_mock.assert_called_once_with( + task, mock.ANY, CONF.pxe.pxe_config_template, + ipxe_enabled=False) + else: + create_pxe_config_mock.assert_called_once_with( + task, mock.ANY, CONF.pxe.uefi_pxe_config_template, + ipxe_enabled=False) + if uefi: + boot_mode = 'uefi' + else: + boot_mode = 'bios' + switch_pxe_config_mock.assert_called_once_with( pxe_config_path, None, - 'bios', False, ipxe_enabled=False, iscsi_boot=False, + boot_mode, False, ipxe_enabled=False, iscsi_boot=False, ramdisk_boot=True, anaconda_boot=False) set_boot_device_mock.assert_called_once_with(task, boot_devices.PXE, @@ -778,6 +842,70 @@ class PXEBootTestCase(db_base.DbTestCase): dhcp_factory_mock.return_value = provider_mock self.node.provision_state = states.DEPLOYING self.config(http_url='http://fake_url', group='deploy') + with task_manager.acquire(self.context, self.node.uuid) as task: + dhcp_opts = pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False) + dhcp_opts += pxe_utils.dhcp_options_for_instance( + task, ipxe_enabled=False, ip_version=6) + pxe_config_path = pxe_utils.get_pxe_config_file_path( + task.node.uuid) + + task.driver.boot.prepare_instance(task) + + get_image_info_mock.assert_called_once_with(task, + ipxe_enabled=False) + cache_mock.assert_called_once_with( + task, image_info, False) + if os.path.isfile('/usr/bin/ksvalidator'): + exec_mock.assert_called_once_with( + 'ksvalidator', mock.ANY, check_on_exit=[0], attempts=1 + ) + provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) + render_mock.assert_called() + write_file_mock.assert_called_with( + '/path/to/ks.cfg', render_mock.return_value + ) + create_pxe_config_mock.assert_called_once_with( + task, mock.ANY, CONF.pxe.uefi_pxe_config_template, + ipxe_enabled=False) + switch_pxe_config_mock.assert_called_once_with( + pxe_config_path, None, + 'uefi', False, ipxe_enabled=False, iscsi_boot=False, + ramdisk_boot=False, anaconda_boot=True) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.PXE, + persistent=True) + + @mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True) + @mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True) + @mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True) + @mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True) + @mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True) + @mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True) + @mock.patch('ironic.drivers.modules.deploy_utils.get_boot_option', + return_value='kickstart', autospec=True) + @mock.patch('ironic.drivers.modules.deploy_utils.get_ironic_api_url', + return_value='http://fakeserver/api', autospec=True) + @mock.patch('ironic.common.utils.render_template', autospec=True) + @mock.patch('ironic.common.utils.write_to_file', autospec=True) + @mock.patch('ironic.common.utils.execute', autospec=True) + def test_prepare_instance_kickstart_bios( + self, exec_mock, write_file_mock, render_mock, api_url_mock, + boot_opt_mock, get_image_info_mock, cache_mock, dhcp_factory_mock, + create_pxe_config_mock, switch_pxe_config_mock, + set_boot_device_mock): + image_info = {'kernel': ['ins_kernel_id', '/path/to/kernel'], + 'ramdisk': ['ins_ramdisk_id', '/path/to/ramdisk'], + 'stage2': ['ins_stage2_id', '/path/to/stage2'], + 'ks_cfg': ['', '/path/to/ks.cfg'], + 'ks_template': ['template_id', '/path/to/ks_template']} + get_image_info_mock.return_value = image_info + provider_mock = mock.MagicMock() + dhcp_factory_mock.return_value = provider_mock + self.node.provision_state = states.DEPLOYING + self.config(http_url='http://fake_url', group='deploy') + self.config(default_boot_mode='bios', group='deploy') + with task_manager.acquire(self.context, self.node.uuid) as task: dhcp_opts = pxe_utils.dhcp_options_for_instance( task, ipxe_enabled=False) diff --git a/ironic/tests/unit/drivers/modules/test_ramdisk.py b/ironic/tests/unit/drivers/modules/test_ramdisk.py index b8e8743791..4deedbfc2a 100644 --- a/ironic/tests/unit/drivers/modules/test_ramdisk.py +++ b/ironic/tests/unit/drivers/modules/test_ramdisk.py @@ -106,8 +106,8 @@ class RamdiskDeployTestCase(db_base.DbTestCase): provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts) switch_pxe_config_mock.assert_called_once_with( pxe_config_path, None, - 'bios', False, ipxe_enabled=False, iscsi_boot=False, - ramdisk_boot=True, anaconda_boot=False) + CONF.deploy.default_boot_mode, False, ipxe_enabled=False, + iscsi_boot=False, ramdisk_boot=True, anaconda_boot=False) set_boot_device_mock.assert_called_once_with(task, boot_devices.PXE, persistent=True) diff --git a/releasenotes/notes/uefi-is-now-the-default-562b0d68adc59008.yaml b/releasenotes/notes/uefi-is-now-the-default-562b0d68adc59008.yaml new file mode 100644 index 0000000000..dc84a5feda --- /dev/null +++ b/releasenotes/notes/uefi-is-now-the-default-562b0d68adc59008.yaml @@ -0,0 +1,16 @@ +--- +features: + - | + The default deployment boot mode is *now* UEFI. Legacy BIOS is still + supported, however operators who require BIOS nodes will need to + set their nodes, or deployment, appropriately. +upgrade: + - | + The default boot mode has been changed and now UEFI. Operators who + were explicitly relying upon BIOS based deployments in the past, + may wish to consider setting an explicit node level override for + the node to only utilize BIOS mode. This can be configured at a + conductor level with the ``[deploy]default_boot_mode``. Options + to set this at a node level can be found in the + `Ironic Installation guide - Advanced features `_ + documentation. diff --git a/zuul.d/ironic-jobs.yaml b/zuul.d/ironic-jobs.yaml index e00e7397cd..bc7ea79106 100644 --- a/zuul.d/ironic-jobs.yaml +++ b/zuul.d/ironic-jobs.yaml @@ -213,9 +213,30 @@ - job: name: ironic-tempest-partition-bios-redfish-pxe description: "Deploy ironic node over PXE using BIOS boot mode" - parent: ironic-base + parent: ironic-tempest-partition-uefi-redfish-vmedia required-projects: - opendev.org/openstack/sushy-tools + vars: + devstack_localrc: + IRONIC_ENABLED_BOOT_INTERFACES: ipxe + SWIFT_ENABLE_TEMPURLS: False + SWIFT_TEMPURL_KEY: '' + # Parent job has a longer timeout due to vmedia usage, + # Reset the callback to a normal-ish value. + IRONIC_CALLBACK_TIMEOUT: 600 + IRONIC_DEFAULT_BOOT_OPTION: netboot + IRONIC_BOOT_MODE: bios + devstack_services: + # Parent job uses swift, this one does not, thus we can turn it off. + s-account: False + s-container: False + s-object: False + s-proxy: False + +- job: + name: ironic-tempest-partition-uefi-redfish-vmedia + description: "Deploy ironic node over Redfish virtual media using UEFI boot mode" + parent: ironic-base vars: devstack_localrc: IRONIC_DEPLOY_DRIVER: redfish @@ -223,19 +244,12 @@ IRONIC_ENABLED_POWER_INTERFACES: redfish IRONIC_ENABLED_MANAGEMENT_INTERFACES: redfish IRONIC_AUTOMATED_CLEAN_ENABLED: False + # TODO(TheJulia): We need to excise netboot from + # jobs at some point. IRONIC_DEFAULT_BOOT_OPTION: netboot - -- job: - name: ironic-tempest-partition-uefi-redfish-vmedia - description: "Deploy ironic node over Redfish virtual media using UEFI boot mode" - parent: ironic-tempest-partition-bios-redfish-pxe - vars: - devstack_localrc: - IRONIC_BOOT_MODE: uefi IRONIC_ENABLED_BOOT_INTERFACES: redfish-virtual-media SWIFT_ENABLE_TEMPURLS: True SWIFT_TEMPURL_KEY: secretkey - IRONIC_AUTOMATED_CLEAN_ENABLED: False # Ironic has to master a new image, and this CAN take longer as a # result and makes this job VERY sensitive to heavy disk IO of the # underlying hypervisor/cloud. @@ -297,6 +311,7 @@ IRONIC_VM_SPECS_RAM: 3096 SWIFT_ENABLE_TEMPURLS: True SWIFT_TEMPURL_KEY: secretkey + IRONIC_BOOT_MODE: bios devstack_services: s-account: True s-container: True @@ -315,6 +330,7 @@ IRONIC_VM_EPHEMERAL_DISK: 0 IRONIC_AUTOMATED_CLEAN_ENABLED: False IRONIC_ENFORCE_SCOPE: True + IRONIC_BOOT_MODE: bios - job: name: ironic-tempest-ipa-partition-uefi-pxe_ipmitool @@ -322,7 +338,6 @@ parent: ironic-base vars: devstack_localrc: - IRONIC_BOOT_MODE: uefi IRONIC_VM_SPECS_RAM: 4096 IRONIC_AUTOMATED_CLEAN_ENABLED: False IRONIC_DEFAULT_BOOT_OPTION: netboot @@ -655,7 +670,6 @@ IRONIC_ENABLED_HARDWARE_TYPES: ipmi IRONIC_ENABLED_BOOT_INTERFACES: pxe IRONIC_IPXE_ENABLED: False - IRONIC_BOOT_MODE: uefi IRONIC_RAMDISK_TYPE: tinyipa IRONIC_AUTOMATED_CLEAN_ENABLED: False IRONIC_DEFAULT_BOOT_OPTION: netboot @@ -700,6 +714,7 @@ IRONIC_AUTOMATED_CLEAN_ENABLED: False SWIFT_ENABLE_TEMPURLS: True SWIFT_TEMPURL_KEY: secretkey + IRONIC_BOOT_MODE: bios - job: name: ironic-tempest-ipxe-ipv6 @@ -777,6 +792,7 @@ IRONIC_VM_EPHEMERAL_DISK: 0 SWIFT_ENABLE_TEMPURLS: True SWIFT_TEMPURL_KEY: secretkey + IRONIC_BOOT_MODE: bios devstack_services: s-account: True s-container: True