diff --git a/ironic/common/images.py b/ironic/common/images.py index 65fd482677..b68911e934 100644 --- a/ironic/common/images.py +++ b/ironic/common/images.py @@ -222,27 +222,33 @@ def create_isolinux_image_for_bios(output_file, kernel, ramdisk, raise exception.ImageCreationFailed(image_type='iso', error=e) -def create_isolinux_image_for_uefi(output_file, deploy_iso, kernel, ramdisk, +def create_isolinux_image_for_uefi(output_file, kernel, ramdisk, + deploy_iso=None, esp_image=None, kernel_params=None): """Creates an isolinux image on the specified file. - Copies the provided kernel, ramdisk, efiboot.img to a directory, creates - the path for grub config file, generates the isolinux configuration file - using the kernel parameters provided, generates the grub configuration - file using kernel parameters and then generates a bootable ISO image - for uefi. + Copies the provided kernel, ramdisk and EFI system partition image to + a directory, generates the grub configuration file using kernel parameters + and then generates a bootable ISO image for UEFI. :param output_file: the path to the file where the iso image needs to be created. - :param deploy_iso: deploy iso used to initiate the deploy. :param kernel: the kernel to use. :param ramdisk: the ramdisk to use. + :param deploy_iso: deploy ISO image to extract EFI system partition image + from. If not specified, the `esp_image` option is required. + :param esp_image: FAT12/16/32-formatted EFI system partition image + containing the EFI boot loader (e.g. GRUB2) for each hardware + architecture to boot. This image will be embedded into the ISO image. + If not specified, the `deploy_iso` option is required. :param kernel_params: a list of strings(each element being a string like 'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added as the kernel cmdline. :raises: ImageCreationFailed, if image creation failed while copying files or while running command to generate iso. """ + EFIBOOT_LOCATION = 'boot/grub/efiboot.img' + grub_options = {'linux': '/vmlinuz', 'initrd': '/initrd'} with utils.tempdir() as tmpdir: @@ -251,26 +257,48 @@ def create_isolinux_image_for_uefi(output_file, deploy_iso, kernel, ramdisk, ramdisk: 'initrd', } - # Open the deploy iso used to initiate deploy and copy the - # efiboot.img i.e. boot loader to the current temporary - # directory. with utils.tempdir() as mountdir: - uefi_path_info, e_img_rel_path, grub_rel_path = ( - _mount_deploy_iso(deploy_iso, mountdir)) + # Open the deploy iso used to initiate deploy and copy the + # efiboot.img i.e. boot loader to the current temporary + # directory. + if deploy_iso: + uefi_path_info, e_img_rel_path, grub_rel_path = ( + _mount_deploy_iso(deploy_iso, mountdir)) + + grub_cfg = os.path.join(tmpdir, grub_rel_path) + + # Use ELF boot loader provided + elif esp_image: + e_img_rel_path = EFIBOOT_LOCATION + grub_rel_path = CONF.grub_config_path.strip() + while grub_rel_path.startswith(os.sep): + grub_rel_path = grub_rel_path[1:] + grub_cfg = os.path.join(tmpdir, grub_rel_path) + + uefi_path_info = { + esp_image: e_img_rel_path, + grub_cfg: grub_rel_path + } + + else: + raise exception.ImageCreationFailed( + image_type='iso', + error='Neither `deploy_iso` nor `esp_image` configured') - # if either of these variables are not initialized then the - # uefi efiboot.img cannot be created. files_info.update(uefi_path_info) + try: _create_root_fs(tmpdir, files_info) + except (OSError, IOError) as e: LOG.exception("Creating the filesystem root failed.") raise exception.ImageCreationFailed(image_type='iso', error=e) + finally: - _umount_without_raise(mountdir) + if deploy_iso: + _umount_without_raise(mountdir) # Generate and copy grub config file. - grub_cfg = os.path.join(tmpdir, grub_rel_path) grub_conf = _generate_cfg(kernel_params, CONF.grub_config_template, grub_options) utils.write_to_file(grub_cfg, grub_conf) @@ -401,8 +429,8 @@ def get_temp_url_for_glance_image(context, image_uuid): def create_boot_iso(context, output_filename, kernel_href, - ramdisk_href, deploy_iso_href, root_uuid=None, - kernel_params=None, boot_mode=None): + ramdisk_href, deploy_iso_href=None, esp_image_href=None, + root_uuid=None, kernel_params=None, boot_mode=None): """Creates a bootable ISO image for a node. Given the hrefs for kernel, ramdisk, root partition's UUID and @@ -414,8 +442,15 @@ def create_boot_iso(context, output_filename, kernel_href, :param output_filename: the absolute path of the output ISO file :param kernel_href: URL or glance uuid of the kernel to use :param ramdisk_href: URL or glance uuid of the ramdisk to use - :param deploy_iso_href: URL or glance uuid of the deploy iso used - :param root_uuid: uuid of the root filesystem (optional) + :param deploy_iso_href: URL or glance UUID of the deploy ISO image + to extract EFI system partition image. If not specified, + the `esp_image_href` option must be present if UEFI-bootable + ISO is desired. + :param esp_image_href: URL or glance UUID of FAT12/16/32-formatted EFI + system partition image containing the EFI boot loader (e.g. GRUB2) + for each hardware architecture to boot. This image will be embedded + into the ISO image. If not specified, the `deploy_iso_href` option + is only required for building UEFI-bootable ISO. :param kernel_params: a string containing whitespace separated values kernel cmdline arguments of the form K=V or K (optional). :boot_mode: the boot mode in which the deploy is to happen. @@ -424,6 +459,7 @@ def create_boot_iso(context, output_filename, kernel_href, with utils.tempdir() as tmpdir: kernel_path = os.path.join(tmpdir, kernel_href.split('/')[-1]) ramdisk_path = os.path.join(tmpdir, ramdisk_href.split('/')[-1]) + fetch(context, kernel_href, kernel_path) fetch(context, ramdisk_href, ramdisk_path) @@ -434,13 +470,28 @@ def create_boot_iso(context, output_filename, kernel_href, params.append(kernel_params) if boot_mode == 'uefi': - deploy_iso = os.path.join(tmpdir, deploy_iso_href.split('/')[-1]) - fetch(context, deploy_iso_href, deploy_iso) + + deploy_iso_path = esp_image_path = None + + if deploy_iso_href: + deploy_iso_path = os.path.join( + tmpdir, deploy_iso_href.split('/')[-1]) + fetch(context, deploy_iso_href, deploy_iso_path) + + elif esp_image_href: + esp_image_path = os.path.join( + tmpdir, esp_image_href.split('/')[-1]) + fetch(context, esp_image_href, esp_image_path) + + elif CONF.esp_image: + esp_image_path = CONF.esp_image + create_isolinux_image_for_uefi(output_filename, - deploy_iso, kernel_path, ramdisk_path, - params) + deploy_iso=deploy_iso_path, + esp_image=esp_image_path, + kernel_params=params) else: create_isolinux_image_for_bios(output_filename, kernel_path, diff --git a/ironic/conf/default.py b/ironic/conf/default.py index 86ec8c06df..e05db1ef8f 100644 --- a/ironic/conf/default.py +++ b/ironic/conf/default.py @@ -210,6 +210,10 @@ image_opts = [ default=os.path.join('$pybasedir', 'common/isolinux_config.template'), help=_('Template file for isolinux configuration file.')), + cfg.StrOpt('grub_config_path', + default='/boot/grub/grub.cfg', + help=_('GRUB2 configuration file location on the UEFI ISO ' + 'images produced by ironic.')), cfg.StrOpt('grub_config_template', default=os.path.join('$pybasedir', 'common/grub_conf.template'), @@ -220,6 +224,17 @@ image_opts = [ 'looked for in ' '"/usr/lib/syslinux/modules/bios/ldlinux.c32" and ' '"/usr/share/syslinux/ldlinux.c32".')), + cfg.StrOpt('esp_image', + help=_('Path to EFI System Partition image file. This file is ' + 'recommended for creating UEFI bootable ISO images ' + 'efficiently. ESP image should contain a ' + 'FAT12/16/32-formatted file system holding EFI boot ' + 'loaders (e.g. GRUB2) for each hardware architecture ' + 'ironic needs to boot. If not configured, ironic ' + 'will attempt to fetch ESP image from some remote ' + 'store (if configured) or extract ESP image from ' + 'UEFI-bootable deploy ISO image.')), + ] img_cache_opts = [ diff --git a/ironic/drivers/modules/ilo/boot.py b/ironic/drivers/modules/ilo/boot.py index 670c65d952..557a8d257d 100644 --- a/ironic/drivers/modules/ilo/boot.py +++ b/ironic/drivers/modules/ilo/boot.py @@ -198,8 +198,10 @@ def _get_boot_iso(task, root_uuid): boot_iso_tmp_file = fileobj.name images.create_boot_iso(task.context, boot_iso_tmp_file, kernel_href, ramdisk_href, - deploy_iso_uuid, root_uuid, - kernel_params, boot_mode) + deploy_iso_href=deploy_iso_uuid, + root_uuid=root_uuid, + kernel_params=kernel_params, + boot_mode=boot_mode) if CONF.ilo.use_web_server_for_images: boot_iso_url = ( diff --git a/ironic/drivers/modules/irmc/boot.py b/ironic/drivers/modules/irmc/boot.py index 4acfa06011..677f56048d 100644 --- a/ironic/drivers/modules/irmc/boot.py +++ b/ironic/drivers/modules/irmc/boot.py @@ -314,8 +314,10 @@ def _prepare_boot_iso(task, root_uuid): images.create_boot_iso(task.context, boot_iso_fullpathname, kernel_href, ramdisk_href, - deploy_iso_href, root_uuid, - kernel_params, boot_mode) + deploy_iso_href=deploy_iso_href, + root_uuid=root_uuid, + kernel_params=kernel_params, + boot_mode=boot_mode) driver_internal_info['irmc_boot_iso'] = boot_iso_filename diff --git a/ironic/tests/unit/common/test_images.py b/ironic/tests/unit/common/test_images.py index 81c74eb7a0..a62289ab67 100644 --- a/ironic/tests/unit/common/test_images.py +++ b/ironic/tests/unit/common/test_images.py @@ -438,9 +438,9 @@ class FsImageTestCase(base.TestCase): @mock.patch.object(os.path, 'relpath', autospec=True) @mock.patch.object(os, 'walk', autospec=True) @mock.patch.object(utils, 'mount', autospec=True) - def test__mount_deploy_iso_fail_no_efibootimg(self, mount_mock, - walk_mock, relpath_mock, - umount_mock): + def test__mount_deploy_iso_fail_no_esp_imageimg(self, mount_mock, + walk_mock, relpath_mock, + umount_mock): walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', [], ['grub.cfg']), ('/tmpdir1/isolinux', [], ['isolinux.bin', 'isolinux.cfg'])] @@ -489,16 +489,17 @@ class FsImageTestCase(base.TestCase): @mock.patch.object(images, '_mount_deploy_iso', autospec=True) @mock.patch.object(utils, 'tempdir', autospec=True) @mock.patch.object(images, '_generate_cfg', autospec=True) - def test_create_isolinux_image_for_uefi( + def test_create_isolinux_image_for_uefi_with_deploy_iso( self, gen_cfg_mock, tempdir_mock, mount_mock, execute_mock, write_to_file_mock, create_root_fs_mock, umount_mock): files_info = { 'path/to/kernel': 'vmlinuz', 'path/to/ramdisk': 'initrd', - 'path/to/grub': 'relpath/to/grub.cfg', - 'sourceabspath/to/efiboot.img': 'path/to/efiboot.img' + 'sourceabspath/to/efiboot.img': 'path/to/efiboot.img', + 'path/to/grub': 'relpath/to/grub.cfg' } + grubcfg = "grubcfg" grub_file = 'tmpdir/relpath/to/grub.cfg' gen_cfg_mock.side_effect = (grubcfg,) @@ -520,9 +521,10 @@ class FsImageTestCase(base.TestCase): mount_mock.return_value = (uefi_path_info, e_img_rel_path, grub_rel_path) - images.create_isolinux_image_for_uefi('tgt_file', 'path/to/deploy_iso', + images.create_isolinux_image_for_uefi('tgt_file', 'path/to/kernel', 'path/to/ramdisk', + deploy_iso='path/to/deploy_iso', kernel_params=params) mount_mock.assert_called_once_with('path/to/deploy_iso', 'mountdir') create_root_fs_mock.assert_called_once_with('tmpdir', files_info) @@ -534,6 +536,49 @@ class FsImageTestCase(base.TestCase): 'path/to/efiboot.img', '-no-emul-boot', '-o', 'tgt_file', 'tmpdir') umount_mock.assert_called_once_with('mountdir') + @mock.patch.object(images, '_create_root_fs', autospec=True) + @mock.patch.object(utils, 'write_to_file', autospec=True) + @mock.patch.object(utils, 'execute', autospec=True) + @mock.patch.object(utils, 'tempdir', autospec=True) + @mock.patch.object(images, '_generate_cfg', autospec=True) + def test_create_isolinux_image_for_uefi_with_esp_image( + self, gen_cfg_mock, tempdir_mock, execute_mock, + write_to_file_mock, create_root_fs_mock): + + files_info = { + 'path/to/kernel': 'vmlinuz', + 'path/to/ramdisk': 'initrd', + 'sourceabspath/to/efiboot.img': 'boot/grub/efiboot.img', + 'tmpdir/boot/grub/grub.cfg': 'boot/grub/grub.cfg' + } + + grubcfg = "grubcfg" + grub_file = 'tmpdir/boot/grub/grub.cfg' + gen_cfg_mock.side_effect = (grubcfg,) + + params = ['a=b', 'c'] + grub_options = {'linux': '/vmlinuz', + 'initrd': '/initrd'} + + mock_file_handle = mock.MagicMock(spec=file) + mock_file_handle.__enter__.return_value = 'tmpdir' + mock_file_handle1 = mock.MagicMock(spec=file) + mock_file_handle1.__enter__.return_value = 'mountdir' + tempdir_mock.side_effect = mock_file_handle, mock_file_handle1 + + images.create_isolinux_image_for_uefi( + 'tgt_file', 'path/to/kernel', 'path/to/ramdisk', + esp_image='sourceabspath/to/efiboot.img', + kernel_params=params) + create_root_fs_mock.assert_called_once_with('tmpdir', files_info) + gen_cfg_mock.assert_any_call(params, CONF.grub_config_template, + grub_options) + write_to_file_mock.assert_any_call(grub_file, grubcfg) + execute_mock.assert_called_once_with( + 'mkisofs', '-r', '-V', 'VMEDIA_BOOT_ISO', '-l', '-e', + 'boot/grub/efiboot.img', '-no-emul-boot', '-o', 'tgt_file', + 'tmpdir') + @mock.patch.object(images, '_create_root_fs', autospec=True) @mock.patch.object(utils, 'write_to_file', autospec=True) @mock.patch.object(utils, 'tempdir', autospec=True) @@ -615,9 +660,10 @@ class FsImageTestCase(base.TestCase): self.assertRaises(exception.ImageCreationFailed, images.create_isolinux_image_for_uefi, - 'tgt_file', 'path/to/deployiso', + 'tgt_file', 'path/to/kernel', - 'path/to/ramdisk') + 'path/to/ramdisk', + deploy_iso='path/to/deployiso') umount_mock.assert_called_once_with('mountdir') @mock.patch.object(images, '_create_root_fs', autospec=True) @@ -660,9 +706,10 @@ class FsImageTestCase(base.TestCase): self.assertRaises(exception.ImageCreationFailed, images.create_isolinux_image_for_uefi, - 'tgt_file', 'path/to/deployiso', + 'tgt_file', 'path/to/kernel', - 'path/to/ramdisk') + 'path/to/ramdisk', + deploy_iso='path/to/deployiso') umount_mock.assert_called_once_with('mountdir') @mock.patch.object(images, '_create_root_fs', autospec=True) @@ -689,15 +736,17 @@ class FsImageTestCase(base.TestCase): @mock.patch.object(images, 'create_isolinux_image_for_uefi', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @mock.patch.object(utils, 'tempdir', autospec=True) - def test_create_boot_iso_for_uefi( + def test_create_boot_iso_for_uefi_deploy_iso( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle - images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', - 'ramdisk-uuid', 'deploy_iso-uuid', - 'root-uuid', 'kernel-params', 'uefi') + images.create_boot_iso( + 'ctx', 'output_file', 'kernel-uuid', + 'ramdisk-uuid', deploy_iso_href='deploy_iso-uuid', + root_uuid='root-uuid', kernel_params='kernel-params', + boot_mode='uefi') fetch_images_mock.assert_any_call( 'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') @@ -708,21 +757,53 @@ class FsImageTestCase(base.TestCase): params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( - 'output_file', 'tmpdir/deploy_iso-uuid', 'tmpdir/kernel-uuid', - 'tmpdir/ramdisk-uuid', params) + 'output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', + deploy_iso='tmpdir/deploy_iso-uuid', esp_image=None, + kernel_params=params) @mock.patch.object(images, 'create_isolinux_image_for_uefi', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @mock.patch.object(utils, 'tempdir', autospec=True) - def test_create_boot_iso_for_uefi_for_hrefs( + def test_create_boot_iso_for_uefi_esp_image( self, tempdir_mock, fetch_images_mock, create_isolinux_mock): mock_file_handle = mock.MagicMock(spec=file) mock_file_handle.__enter__.return_value = 'tmpdir' tempdir_mock.return_value = mock_file_handle - images.create_boot_iso('ctx', 'output_file', 'http://kernel-href', - 'http://ramdisk-href', 'http://deploy_iso-href', - 'root-uuid', 'kernel-params', 'uefi') + images.create_boot_iso( + 'ctx', 'output_file', 'kernel-uuid', + 'ramdisk-uuid', esp_image_href='efiboot-uuid', + root_uuid='root-uuid', kernel_params='kernel-params', + boot_mode='uefi') + + fetch_images_mock.assert_any_call( + 'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') + fetch_images_mock.assert_any_call( + 'ctx', 'ramdisk-uuid', 'tmpdir/ramdisk-uuid') + fetch_images_mock.assert_any_call( + 'ctx', 'efiboot-uuid', 'tmpdir/efiboot-uuid') + + params = ['root=UUID=root-uuid', 'kernel-params'] + create_isolinux_mock.assert_called_once_with( + 'output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid', + deploy_iso=None, esp_image='tmpdir/efiboot-uuid', + kernel_params=params) + + @mock.patch.object(images, 'create_isolinux_image_for_uefi', autospec=True) + @mock.patch.object(images, 'fetch', autospec=True) + @mock.patch.object(utils, 'tempdir', autospec=True) + def test_create_boot_iso_for_uefi_deploy_iso_for_hrefs( + self, tempdir_mock, fetch_images_mock, create_isolinux_mock): + mock_file_handle = mock.MagicMock(spec=file) + mock_file_handle.__enter__.return_value = 'tmpdir' + tempdir_mock.return_value = mock_file_handle + + images.create_boot_iso( + 'ctx', 'output_file', 'http://kernel-href', 'http://ramdisk-href', + deploy_iso_href='http://deploy_iso-href', + root_uuid='root-uuid', kernel_params='kernel-params', + boot_mode='uefi') + expected_calls = [mock.call('ctx', 'http://kernel-href', 'tmpdir/kernel-href'), mock.call('ctx', 'http://ramdisk-href', @@ -732,8 +813,37 @@ class FsImageTestCase(base.TestCase): fetch_images_mock.assert_has_calls(expected_calls) params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( - 'output_file', 'tmpdir/deploy_iso-href', 'tmpdir/kernel-href', - 'tmpdir/ramdisk-href', params) + 'output_file', 'tmpdir/kernel-href', 'tmpdir/ramdisk-href', + deploy_iso='tmpdir/deploy_iso-href', esp_image=None, + kernel_params=params) + + @mock.patch.object(images, 'create_isolinux_image_for_uefi', autospec=True) + @mock.patch.object(images, 'fetch', autospec=True) + @mock.patch.object(utils, 'tempdir', autospec=True) + def test_create_boot_iso_for_uefi_esp_image_for_hrefs( + self, tempdir_mock, fetch_images_mock, create_isolinux_mock): + mock_file_handle = mock.MagicMock(spec=file) + mock_file_handle.__enter__.return_value = 'tmpdir' + tempdir_mock.return_value = mock_file_handle + + images.create_boot_iso( + 'ctx', 'output_file', 'http://kernel-href', 'http://ramdisk-href', + esp_image_href='http://efiboot-href', + root_uuid='root-uuid', kernel_params='kernel-params', + boot_mode='uefi') + + expected_calls = [mock.call('ctx', 'http://kernel-href', + 'tmpdir/kernel-href'), + mock.call('ctx', 'http://ramdisk-href', + 'tmpdir/ramdisk-href'), + mock.call('ctx', 'http://efiboot-href', + 'tmpdir/efiboot-href')] + fetch_images_mock.assert_has_calls(expected_calls) + params = ['root=UUID=root-uuid', 'kernel-params'] + create_isolinux_mock.assert_called_once_with( + 'output_file', 'tmpdir/kernel-href', 'tmpdir/ramdisk-href', + deploy_iso=None, esp_image='tmpdir/efiboot-href', + kernel_params=params) @mock.patch.object(images, 'create_isolinux_image_for_bios', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -746,7 +856,8 @@ class FsImageTestCase(base.TestCase): images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', - 'root-uuid', 'kernel-params', 'bios') + 'efiboot-uuid', 'root-uuid', 'kernel-params', + 'bios') fetch_images_mock.assert_any_call( 'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') @@ -777,7 +888,8 @@ class FsImageTestCase(base.TestCase): images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', - 'root-uuid', 'kernel-params', None) + 'efiboot-uuid', 'root-uuid', 'kernel-params', + None) fetch_images_mock.assert_any_call( 'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid') diff --git a/ironic/tests/unit/drivers/modules/ilo/test_boot.py b/ironic/tests/unit/drivers/modules/ilo/test_boot.py index f789a44519..f85f79a08b 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_boot.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_boot.py @@ -204,14 +204,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest): task.context, 'image-uuid', ['boot_iso', 'kernel_id', 'ramdisk_id']) boot_object_name_mock.assert_called_once_with(task.node) - create_boot_iso_mock.assert_called_once_with(task.context, - 'tmpfile', - 'kernel_uuid', - 'ramdisk_uuid', - 'deploy_iso_uuid', - 'root-uuid', - 'kernel-params', - 'uefi') + create_boot_iso_mock.assert_called_once_with( + task.context, 'tmpfile', 'kernel_uuid', 'ramdisk_uuid', + deploy_iso_href='deploy_iso_uuid', + root_uuid='root-uuid', + kernel_params='kernel-params', + boot_mode='uefi') swift_obj_mock.create_object.assert_called_once_with('ilo-cont', 'abcdef', 'tmpfile') @@ -273,14 +271,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest): task.context, 'image-uuid', ['boot_iso', 'kernel_id', 'ramdisk_id']) boot_object_name_mock.assert_called_once_with(task.node) - create_boot_iso_mock.assert_called_once_with(task.context, - 'tmpfile', - kernel_href, - ramdisk_href, - 'deploy_iso_uuid', - 'root-uuid', - 'kernel-params', - 'uefi') + create_boot_iso_mock.assert_called_once_with( + task.context, 'tmpfile', kernel_href, ramdisk_href, + deploy_iso_href='deploy_iso_uuid', + root_uuid='root-uuid', + kernel_params='kernel-params', + boot_mode='uefi') boot_iso_expected = 'http://10.10.1.30/httpboot/new_boot_iso' self.assertEqual(boot_iso_expected, boot_iso_actual) copy_file_mock.assert_called_once_with(fileobj_mock.name, @@ -336,14 +332,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest): task.context, 'image-uuid', ['boot_iso', 'kernel_id', 'ramdisk_id']) boot_object_name_mock.assert_called_once_with(task.node) - create_boot_iso_mock.assert_called_once_with(task.context, - 'tmpfile', - kernel_href, - ramdisk_href, - 'deploy_iso_uuid', - 'root-uuid', - 'kernel-params', - 'uefi') + create_boot_iso_mock.assert_called_once_with( + task.context, 'tmpfile', kernel_href, ramdisk_href, + deploy_iso_href='deploy_iso_uuid', + root_uuid='root-uuid', + kernel_params='kernel-params', + boot_mode='uefi') boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef' self.assertEqual(boot_iso_expected, boot_iso_actual) copy_file_mock.assert_called_once_with(fileobj_mock.name, diff --git a/ironic/tests/unit/drivers/modules/irmc/test_boot.py b/ironic/tests/unit/drivers/modules/irmc/test_boot.py index dfd1af84c2..f368d7fd94 100644 --- a/ironic/tests/unit/drivers/modules/irmc/test_boot.py +++ b/ironic/tests/unit/drivers/modules/irmc/test_boot.py @@ -498,8 +498,9 @@ class IRMCDeployPrivateMethodsTestCase(test_common.BaseIRMCTest): '/remote_image_share_root/' "boot-%s.iso" % self.node.uuid, 'kernel_uuid', 'ramdisk_uuid', - '02f9d414-2ce0-4cf5-b48f-dbc1bf678f55', - 'root-uuid', 'kernel-params', 'uefi') + deploy_iso_href='02f9d414-2ce0-4cf5-b48f-dbc1bf678f55', + root_uuid='root-uuid', kernel_params='kernel-params', + boot_mode='uefi') task.node.refresh() self.assertEqual("boot-%s.iso" % self.node.uuid, task.node.driver_internal_info['irmc_boot_iso']) diff --git a/releasenotes/notes/build-iso-from-esp-d156036aa8ef85fb.yaml b/releasenotes/notes/build-iso-from-esp-d156036aa8ef85fb.yaml new file mode 100644 index 0000000000..b7871fd21e --- /dev/null +++ b/releasenotes/notes/build-iso-from-esp-d156036aa8ef85fb.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + Allows the user to supply EFI system partition image to ironic, for + building UEFI-bootable ISO images, in form of a local file or UUID + or URI reference. The new ``[conductor]esp_image`` option can be used + to configure ironic to use local file. +other: + - | + Makes ironic building UEFI-only bootable ISO image (when being asked to + build a UEFI-bootable image) rather than building a hybrid + BIOS/UEFI-bootable ISO.