Build ISO out of EFI system partition image
When ironic builds UEFI-bootable ISO image, it extracts EFI system partition image (`efiboot.img`) from the `deploy_iso` ISO image. This change allows supplying EFI system partition image to the ISO image building routines in form of a local file or UUID or URI reference. The motivation behind this change is to make UEFI-bootable image building process more efficient and functional. This change is thought of as a prerequisite for the upcoming Redfish-based virtual media boot feature. Story: 1526753 Task: 28098 Change-Id: Idf912ff2146434b666fdb4250dc1ecad39bc5a04
This commit is contained in:
parent
53823d7f93
commit
738d4eafdc
@ -222,27 +222,33 @@ def create_isolinux_image_for_bios(output_file, kernel, ramdisk,
|
|||||||
raise exception.ImageCreationFailed(image_type='iso', error=e)
|
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):
|
kernel_params=None):
|
||||||
"""Creates an isolinux image on the specified file.
|
"""Creates an isolinux image on the specified file.
|
||||||
|
|
||||||
Copies the provided kernel, ramdisk, efiboot.img to a directory, creates
|
Copies the provided kernel, ramdisk and EFI system partition image to
|
||||||
the path for grub config file, generates the isolinux configuration file
|
a directory, generates the grub configuration file using kernel parameters
|
||||||
using the kernel parameters provided, generates the grub configuration
|
and then generates a bootable ISO image for UEFI.
|
||||||
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
|
:param output_file: the path to the file where the iso image needs to be
|
||||||
created.
|
created.
|
||||||
:param deploy_iso: deploy iso used to initiate the deploy.
|
|
||||||
:param kernel: the kernel to use.
|
:param kernel: the kernel to use.
|
||||||
:param ramdisk: the ramdisk 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
|
: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
|
'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added
|
||||||
as the kernel cmdline.
|
as the kernel cmdline.
|
||||||
:raises: ImageCreationFailed, if image creation failed while copying files
|
:raises: ImageCreationFailed, if image creation failed while copying files
|
||||||
or while running command to generate iso.
|
or while running command to generate iso.
|
||||||
"""
|
"""
|
||||||
|
EFIBOOT_LOCATION = 'boot/grub/efiboot.img'
|
||||||
|
|
||||||
grub_options = {'linux': '/vmlinuz', 'initrd': '/initrd'}
|
grub_options = {'linux': '/vmlinuz', 'initrd': '/initrd'}
|
||||||
|
|
||||||
with utils.tempdir() as tmpdir:
|
with utils.tempdir() as tmpdir:
|
||||||
@ -251,26 +257,48 @@ def create_isolinux_image_for_uefi(output_file, deploy_iso, kernel, ramdisk,
|
|||||||
ramdisk: 'initrd',
|
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:
|
with utils.tempdir() as mountdir:
|
||||||
uefi_path_info, e_img_rel_path, grub_rel_path = (
|
# Open the deploy iso used to initiate deploy and copy the
|
||||||
_mount_deploy_iso(deploy_iso, mountdir))
|
# 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)
|
files_info.update(uefi_path_info)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_create_root_fs(tmpdir, files_info)
|
_create_root_fs(tmpdir, files_info)
|
||||||
|
|
||||||
except (OSError, IOError) as e:
|
except (OSError, IOError) as e:
|
||||||
LOG.exception("Creating the filesystem root failed.")
|
LOG.exception("Creating the filesystem root failed.")
|
||||||
raise exception.ImageCreationFailed(image_type='iso', error=e)
|
raise exception.ImageCreationFailed(image_type='iso', error=e)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
_umount_without_raise(mountdir)
|
if deploy_iso:
|
||||||
|
_umount_without_raise(mountdir)
|
||||||
|
|
||||||
# Generate and copy grub config file.
|
# Generate and copy grub config file.
|
||||||
grub_cfg = os.path.join(tmpdir, grub_rel_path)
|
|
||||||
grub_conf = _generate_cfg(kernel_params,
|
grub_conf = _generate_cfg(kernel_params,
|
||||||
CONF.grub_config_template, grub_options)
|
CONF.grub_config_template, grub_options)
|
||||||
utils.write_to_file(grub_cfg, grub_conf)
|
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,
|
def create_boot_iso(context, output_filename, kernel_href,
|
||||||
ramdisk_href, deploy_iso_href, root_uuid=None,
|
ramdisk_href, deploy_iso_href=None, esp_image_href=None,
|
||||||
kernel_params=None, boot_mode=None):
|
root_uuid=None, kernel_params=None, boot_mode=None):
|
||||||
"""Creates a bootable ISO image for a node.
|
"""Creates a bootable ISO image for a node.
|
||||||
|
|
||||||
Given the hrefs for kernel, ramdisk, root partition's UUID and
|
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 output_filename: the absolute path of the output ISO file
|
||||||
:param kernel_href: URL or glance uuid of the kernel to use
|
: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 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 deploy_iso_href: URL or glance UUID of the deploy ISO image
|
||||||
:param root_uuid: uuid of the root filesystem (optional)
|
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
|
:param kernel_params: a string containing whitespace separated values
|
||||||
kernel cmdline arguments of the form K=V or K (optional).
|
kernel cmdline arguments of the form K=V or K (optional).
|
||||||
:boot_mode: the boot mode in which the deploy is to happen.
|
: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:
|
with utils.tempdir() as tmpdir:
|
||||||
kernel_path = os.path.join(tmpdir, kernel_href.split('/')[-1])
|
kernel_path = os.path.join(tmpdir, kernel_href.split('/')[-1])
|
||||||
ramdisk_path = os.path.join(tmpdir, ramdisk_href.split('/')[-1])
|
ramdisk_path = os.path.join(tmpdir, ramdisk_href.split('/')[-1])
|
||||||
|
|
||||||
fetch(context, kernel_href, kernel_path)
|
fetch(context, kernel_href, kernel_path)
|
||||||
fetch(context, ramdisk_href, ramdisk_path)
|
fetch(context, ramdisk_href, ramdisk_path)
|
||||||
|
|
||||||
@ -434,13 +470,28 @@ def create_boot_iso(context, output_filename, kernel_href,
|
|||||||
params.append(kernel_params)
|
params.append(kernel_params)
|
||||||
|
|
||||||
if boot_mode == 'uefi':
|
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,
|
create_isolinux_image_for_uefi(output_filename,
|
||||||
deploy_iso,
|
|
||||||
kernel_path,
|
kernel_path,
|
||||||
ramdisk_path,
|
ramdisk_path,
|
||||||
params)
|
deploy_iso=deploy_iso_path,
|
||||||
|
esp_image=esp_image_path,
|
||||||
|
kernel_params=params)
|
||||||
else:
|
else:
|
||||||
create_isolinux_image_for_bios(output_filename,
|
create_isolinux_image_for_bios(output_filename,
|
||||||
kernel_path,
|
kernel_path,
|
||||||
|
@ -210,6 +210,10 @@ image_opts = [
|
|||||||
default=os.path.join('$pybasedir',
|
default=os.path.join('$pybasedir',
|
||||||
'common/isolinux_config.template'),
|
'common/isolinux_config.template'),
|
||||||
help=_('Template file for isolinux configuration file.')),
|
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',
|
cfg.StrOpt('grub_config_template',
|
||||||
default=os.path.join('$pybasedir',
|
default=os.path.join('$pybasedir',
|
||||||
'common/grub_conf.template'),
|
'common/grub_conf.template'),
|
||||||
@ -220,6 +224,17 @@ image_opts = [
|
|||||||
'looked for in '
|
'looked for in '
|
||||||
'"/usr/lib/syslinux/modules/bios/ldlinux.c32" and '
|
'"/usr/lib/syslinux/modules/bios/ldlinux.c32" and '
|
||||||
'"/usr/share/syslinux/ldlinux.c32".')),
|
'"/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 = [
|
img_cache_opts = [
|
||||||
|
@ -198,8 +198,10 @@ def _get_boot_iso(task, root_uuid):
|
|||||||
boot_iso_tmp_file = fileobj.name
|
boot_iso_tmp_file = fileobj.name
|
||||||
images.create_boot_iso(task.context, boot_iso_tmp_file,
|
images.create_boot_iso(task.context, boot_iso_tmp_file,
|
||||||
kernel_href, ramdisk_href,
|
kernel_href, ramdisk_href,
|
||||||
deploy_iso_uuid, root_uuid,
|
deploy_iso_href=deploy_iso_uuid,
|
||||||
kernel_params, boot_mode)
|
root_uuid=root_uuid,
|
||||||
|
kernel_params=kernel_params,
|
||||||
|
boot_mode=boot_mode)
|
||||||
|
|
||||||
if CONF.ilo.use_web_server_for_images:
|
if CONF.ilo.use_web_server_for_images:
|
||||||
boot_iso_url = (
|
boot_iso_url = (
|
||||||
|
@ -314,8 +314,10 @@ def _prepare_boot_iso(task, root_uuid):
|
|||||||
|
|
||||||
images.create_boot_iso(task.context, boot_iso_fullpathname,
|
images.create_boot_iso(task.context, boot_iso_fullpathname,
|
||||||
kernel_href, ramdisk_href,
|
kernel_href, ramdisk_href,
|
||||||
deploy_iso_href, root_uuid,
|
deploy_iso_href=deploy_iso_href,
|
||||||
kernel_params, boot_mode)
|
root_uuid=root_uuid,
|
||||||
|
kernel_params=kernel_params,
|
||||||
|
boot_mode=boot_mode)
|
||||||
|
|
||||||
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
driver_internal_info['irmc_boot_iso'] = boot_iso_filename
|
||||||
|
|
||||||
|
@ -438,9 +438,9 @@ class FsImageTestCase(base.TestCase):
|
|||||||
@mock.patch.object(os.path, 'relpath', autospec=True)
|
@mock.patch.object(os.path, 'relpath', autospec=True)
|
||||||
@mock.patch.object(os, 'walk', autospec=True)
|
@mock.patch.object(os, 'walk', autospec=True)
|
||||||
@mock.patch.object(utils, 'mount', autospec=True)
|
@mock.patch.object(utils, 'mount', autospec=True)
|
||||||
def test__mount_deploy_iso_fail_no_efibootimg(self, mount_mock,
|
def test__mount_deploy_iso_fail_no_esp_imageimg(self, mount_mock,
|
||||||
walk_mock, relpath_mock,
|
walk_mock, relpath_mock,
|
||||||
umount_mock):
|
umount_mock):
|
||||||
walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', [], ['grub.cfg']),
|
walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', [], ['grub.cfg']),
|
||||||
('/tmpdir1/isolinux', [],
|
('/tmpdir1/isolinux', [],
|
||||||
['isolinux.bin', 'isolinux.cfg'])]
|
['isolinux.bin', 'isolinux.cfg'])]
|
||||||
@ -489,16 +489,17 @@ class FsImageTestCase(base.TestCase):
|
|||||||
@mock.patch.object(images, '_mount_deploy_iso', autospec=True)
|
@mock.patch.object(images, '_mount_deploy_iso', autospec=True)
|
||||||
@mock.patch.object(utils, 'tempdir', autospec=True)
|
@mock.patch.object(utils, 'tempdir', autospec=True)
|
||||||
@mock.patch.object(images, '_generate_cfg', 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,
|
self, gen_cfg_mock, tempdir_mock, mount_mock, execute_mock,
|
||||||
write_to_file_mock, create_root_fs_mock, umount_mock):
|
write_to_file_mock, create_root_fs_mock, umount_mock):
|
||||||
|
|
||||||
files_info = {
|
files_info = {
|
||||||
'path/to/kernel': 'vmlinuz',
|
'path/to/kernel': 'vmlinuz',
|
||||||
'path/to/ramdisk': 'initrd',
|
'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"
|
grubcfg = "grubcfg"
|
||||||
grub_file = 'tmpdir/relpath/to/grub.cfg'
|
grub_file = 'tmpdir/relpath/to/grub.cfg'
|
||||||
gen_cfg_mock.side_effect = (grubcfg,)
|
gen_cfg_mock.side_effect = (grubcfg,)
|
||||||
@ -520,9 +521,10 @@ class FsImageTestCase(base.TestCase):
|
|||||||
mount_mock.return_value = (uefi_path_info,
|
mount_mock.return_value = (uefi_path_info,
|
||||||
e_img_rel_path, grub_rel_path)
|
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/kernel',
|
||||||
'path/to/ramdisk',
|
'path/to/ramdisk',
|
||||||
|
deploy_iso='path/to/deploy_iso',
|
||||||
kernel_params=params)
|
kernel_params=params)
|
||||||
mount_mock.assert_called_once_with('path/to/deploy_iso', 'mountdir')
|
mount_mock.assert_called_once_with('path/to/deploy_iso', 'mountdir')
|
||||||
create_root_fs_mock.assert_called_once_with('tmpdir', files_info)
|
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')
|
'path/to/efiboot.img', '-no-emul-boot', '-o', 'tgt_file', 'tmpdir')
|
||||||
umount_mock.assert_called_once_with('mountdir')
|
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(images, '_create_root_fs', autospec=True)
|
||||||
@mock.patch.object(utils, 'write_to_file', autospec=True)
|
@mock.patch.object(utils, 'write_to_file', autospec=True)
|
||||||
@mock.patch.object(utils, 'tempdir', autospec=True)
|
@mock.patch.object(utils, 'tempdir', autospec=True)
|
||||||
@ -615,9 +660,10 @@ class FsImageTestCase(base.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(exception.ImageCreationFailed,
|
self.assertRaises(exception.ImageCreationFailed,
|
||||||
images.create_isolinux_image_for_uefi,
|
images.create_isolinux_image_for_uefi,
|
||||||
'tgt_file', 'path/to/deployiso',
|
'tgt_file',
|
||||||
'path/to/kernel',
|
'path/to/kernel',
|
||||||
'path/to/ramdisk')
|
'path/to/ramdisk',
|
||||||
|
deploy_iso='path/to/deployiso')
|
||||||
umount_mock.assert_called_once_with('mountdir')
|
umount_mock.assert_called_once_with('mountdir')
|
||||||
|
|
||||||
@mock.patch.object(images, '_create_root_fs', autospec=True)
|
@mock.patch.object(images, '_create_root_fs', autospec=True)
|
||||||
@ -660,9 +706,10 @@ class FsImageTestCase(base.TestCase):
|
|||||||
|
|
||||||
self.assertRaises(exception.ImageCreationFailed,
|
self.assertRaises(exception.ImageCreationFailed,
|
||||||
images.create_isolinux_image_for_uefi,
|
images.create_isolinux_image_for_uefi,
|
||||||
'tgt_file', 'path/to/deployiso',
|
'tgt_file',
|
||||||
'path/to/kernel',
|
'path/to/kernel',
|
||||||
'path/to/ramdisk')
|
'path/to/ramdisk',
|
||||||
|
deploy_iso='path/to/deployiso')
|
||||||
umount_mock.assert_called_once_with('mountdir')
|
umount_mock.assert_called_once_with('mountdir')
|
||||||
|
|
||||||
@mock.patch.object(images, '_create_root_fs', autospec=True)
|
@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, 'create_isolinux_image_for_uefi', autospec=True)
|
||||||
@mock.patch.object(images, 'fetch', autospec=True)
|
@mock.patch.object(images, 'fetch', autospec=True)
|
||||||
@mock.patch.object(utils, 'tempdir', 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):
|
self, tempdir_mock, fetch_images_mock, create_isolinux_mock):
|
||||||
mock_file_handle = mock.MagicMock(spec=file)
|
mock_file_handle = mock.MagicMock(spec=file)
|
||||||
mock_file_handle.__enter__.return_value = 'tmpdir'
|
mock_file_handle.__enter__.return_value = 'tmpdir'
|
||||||
tempdir_mock.return_value = mock_file_handle
|
tempdir_mock.return_value = mock_file_handle
|
||||||
|
|
||||||
images.create_boot_iso('ctx', 'output_file', 'kernel-uuid',
|
images.create_boot_iso(
|
||||||
'ramdisk-uuid', 'deploy_iso-uuid',
|
'ctx', 'output_file', 'kernel-uuid',
|
||||||
'root-uuid', 'kernel-params', 'uefi')
|
'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(
|
fetch_images_mock.assert_any_call(
|
||||||
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
||||||
@ -708,21 +757,53 @@ class FsImageTestCase(base.TestCase):
|
|||||||
|
|
||||||
params = ['root=UUID=root-uuid', 'kernel-params']
|
params = ['root=UUID=root-uuid', 'kernel-params']
|
||||||
create_isolinux_mock.assert_called_once_with(
|
create_isolinux_mock.assert_called_once_with(
|
||||||
'output_file', 'tmpdir/deploy_iso-uuid', 'tmpdir/kernel-uuid',
|
'output_file', 'tmpdir/kernel-uuid', 'tmpdir/ramdisk-uuid',
|
||||||
'tmpdir/ramdisk-uuid', params)
|
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, 'create_isolinux_image_for_uefi', autospec=True)
|
||||||
@mock.patch.object(images, 'fetch', autospec=True)
|
@mock.patch.object(images, 'fetch', autospec=True)
|
||||||
@mock.patch.object(utils, 'tempdir', 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):
|
self, tempdir_mock, fetch_images_mock, create_isolinux_mock):
|
||||||
mock_file_handle = mock.MagicMock(spec=file)
|
mock_file_handle = mock.MagicMock(spec=file)
|
||||||
mock_file_handle.__enter__.return_value = 'tmpdir'
|
mock_file_handle.__enter__.return_value = 'tmpdir'
|
||||||
tempdir_mock.return_value = mock_file_handle
|
tempdir_mock.return_value = mock_file_handle
|
||||||
|
|
||||||
images.create_boot_iso('ctx', 'output_file', 'http://kernel-href',
|
images.create_boot_iso(
|
||||||
'http://ramdisk-href', 'http://deploy_iso-href',
|
'ctx', 'output_file', 'kernel-uuid',
|
||||||
'root-uuid', 'kernel-params', 'uefi')
|
'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',
|
expected_calls = [mock.call('ctx', 'http://kernel-href',
|
||||||
'tmpdir/kernel-href'),
|
'tmpdir/kernel-href'),
|
||||||
mock.call('ctx', 'http://ramdisk-href',
|
mock.call('ctx', 'http://ramdisk-href',
|
||||||
@ -732,8 +813,37 @@ class FsImageTestCase(base.TestCase):
|
|||||||
fetch_images_mock.assert_has_calls(expected_calls)
|
fetch_images_mock.assert_has_calls(expected_calls)
|
||||||
params = ['root=UUID=root-uuid', 'kernel-params']
|
params = ['root=UUID=root-uuid', 'kernel-params']
|
||||||
create_isolinux_mock.assert_called_once_with(
|
create_isolinux_mock.assert_called_once_with(
|
||||||
'output_file', 'tmpdir/deploy_iso-href', 'tmpdir/kernel-href',
|
'output_file', 'tmpdir/kernel-href', 'tmpdir/ramdisk-href',
|
||||||
'tmpdir/ramdisk-href', params)
|
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, 'create_isolinux_image_for_bios', autospec=True)
|
||||||
@mock.patch.object(images, 'fetch', 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',
|
images.create_boot_iso('ctx', 'output_file', 'kernel-uuid',
|
||||||
'ramdisk-uuid', 'deploy_iso-uuid',
|
'ramdisk-uuid', 'deploy_iso-uuid',
|
||||||
'root-uuid', 'kernel-params', 'bios')
|
'efiboot-uuid', 'root-uuid', 'kernel-params',
|
||||||
|
'bios')
|
||||||
|
|
||||||
fetch_images_mock.assert_any_call(
|
fetch_images_mock.assert_any_call(
|
||||||
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
||||||
@ -777,7 +888,8 @@ class FsImageTestCase(base.TestCase):
|
|||||||
|
|
||||||
images.create_boot_iso('ctx', 'output_file', 'kernel-uuid',
|
images.create_boot_iso('ctx', 'output_file', 'kernel-uuid',
|
||||||
'ramdisk-uuid', 'deploy_iso-uuid',
|
'ramdisk-uuid', 'deploy_iso-uuid',
|
||||||
'root-uuid', 'kernel-params', None)
|
'efiboot-uuid', 'root-uuid', 'kernel-params',
|
||||||
|
None)
|
||||||
|
|
||||||
fetch_images_mock.assert_any_call(
|
fetch_images_mock.assert_any_call(
|
||||||
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
'ctx', 'kernel-uuid', 'tmpdir/kernel-uuid')
|
||||||
|
@ -204,14 +204,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
|
|||||||
task.context, 'image-uuid',
|
task.context, 'image-uuid',
|
||||||
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
||||||
boot_object_name_mock.assert_called_once_with(task.node)
|
boot_object_name_mock.assert_called_once_with(task.node)
|
||||||
create_boot_iso_mock.assert_called_once_with(task.context,
|
create_boot_iso_mock.assert_called_once_with(
|
||||||
'tmpfile',
|
task.context, 'tmpfile', 'kernel_uuid', 'ramdisk_uuid',
|
||||||
'kernel_uuid',
|
deploy_iso_href='deploy_iso_uuid',
|
||||||
'ramdisk_uuid',
|
root_uuid='root-uuid',
|
||||||
'deploy_iso_uuid',
|
kernel_params='kernel-params',
|
||||||
'root-uuid',
|
boot_mode='uefi')
|
||||||
'kernel-params',
|
|
||||||
'uefi')
|
|
||||||
swift_obj_mock.create_object.assert_called_once_with('ilo-cont',
|
swift_obj_mock.create_object.assert_called_once_with('ilo-cont',
|
||||||
'abcdef',
|
'abcdef',
|
||||||
'tmpfile')
|
'tmpfile')
|
||||||
@ -273,14 +271,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
|
|||||||
task.context, 'image-uuid',
|
task.context, 'image-uuid',
|
||||||
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
||||||
boot_object_name_mock.assert_called_once_with(task.node)
|
boot_object_name_mock.assert_called_once_with(task.node)
|
||||||
create_boot_iso_mock.assert_called_once_with(task.context,
|
create_boot_iso_mock.assert_called_once_with(
|
||||||
'tmpfile',
|
task.context, 'tmpfile', kernel_href, ramdisk_href,
|
||||||
kernel_href,
|
deploy_iso_href='deploy_iso_uuid',
|
||||||
ramdisk_href,
|
root_uuid='root-uuid',
|
||||||
'deploy_iso_uuid',
|
kernel_params='kernel-params',
|
||||||
'root-uuid',
|
boot_mode='uefi')
|
||||||
'kernel-params',
|
|
||||||
'uefi')
|
|
||||||
boot_iso_expected = 'http://10.10.1.30/httpboot/new_boot_iso'
|
boot_iso_expected = 'http://10.10.1.30/httpboot/new_boot_iso'
|
||||||
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
||||||
copy_file_mock.assert_called_once_with(fileobj_mock.name,
|
copy_file_mock.assert_called_once_with(fileobj_mock.name,
|
||||||
@ -336,14 +332,12 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
|
|||||||
task.context, 'image-uuid',
|
task.context, 'image-uuid',
|
||||||
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
['boot_iso', 'kernel_id', 'ramdisk_id'])
|
||||||
boot_object_name_mock.assert_called_once_with(task.node)
|
boot_object_name_mock.assert_called_once_with(task.node)
|
||||||
create_boot_iso_mock.assert_called_once_with(task.context,
|
create_boot_iso_mock.assert_called_once_with(
|
||||||
'tmpfile',
|
task.context, 'tmpfile', kernel_href, ramdisk_href,
|
||||||
kernel_href,
|
deploy_iso_href='deploy_iso_uuid',
|
||||||
ramdisk_href,
|
root_uuid='root-uuid',
|
||||||
'deploy_iso_uuid',
|
kernel_params='kernel-params',
|
||||||
'root-uuid',
|
boot_mode='uefi')
|
||||||
'kernel-params',
|
|
||||||
'uefi')
|
|
||||||
boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef'
|
boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef'
|
||||||
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
self.assertEqual(boot_iso_expected, boot_iso_actual)
|
||||||
copy_file_mock.assert_called_once_with(fileobj_mock.name,
|
copy_file_mock.assert_called_once_with(fileobj_mock.name,
|
||||||
|
@ -498,8 +498,9 @@ class IRMCDeployPrivateMethodsTestCase(test_common.BaseIRMCTest):
|
|||||||
'/remote_image_share_root/'
|
'/remote_image_share_root/'
|
||||||
"boot-%s.iso" % self.node.uuid,
|
"boot-%s.iso" % self.node.uuid,
|
||||||
'kernel_uuid', 'ramdisk_uuid',
|
'kernel_uuid', 'ramdisk_uuid',
|
||||||
'02f9d414-2ce0-4cf5-b48f-dbc1bf678f55',
|
deploy_iso_href='02f9d414-2ce0-4cf5-b48f-dbc1bf678f55',
|
||||||
'root-uuid', 'kernel-params', 'uefi')
|
root_uuid='root-uuid', kernel_params='kernel-params',
|
||||||
|
boot_mode='uefi')
|
||||||
task.node.refresh()
|
task.node.refresh()
|
||||||
self.assertEqual("boot-%s.iso" % self.node.uuid,
|
self.assertEqual("boot-%s.iso" % self.node.uuid,
|
||||||
task.node.driver_internal_info['irmc_boot_iso'])
|
task.node.driver_internal_info['irmc_boot_iso'])
|
||||||
|
12
releasenotes/notes/build-iso-from-esp-d156036aa8ef85fb.yaml
Normal file
12
releasenotes/notes/build-iso-from-esp-d156036aa8ef85fb.yaml
Normal file
@ -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.
|
Loading…
x
Reference in New Issue
Block a user