Merge "Use pycdlib to extract deploy iso"

This commit is contained in:
Zuul 2022-03-18 20:55:49 +00:00 committed by Gerrit Code Review
commit 03af6448ab
2 changed files with 71 additions and 67 deletions

View File

@ -27,6 +27,7 @@ from ironic_lib import disk_utils
from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_utils import fileutils
import pycdlib
from ironic.common import exception
from ironic.common.glance_service import service_utils as glance_utils
@ -72,14 +73,6 @@ def _create_root_fs(root_directory, files_info):
shutil.copyfile(src_file, target_file)
def _umount_without_raise(mount_dir):
"""Helper method to umount without raise."""
try:
utils.umount(mount_dir)
except processutils.ProcessExecutionError:
pass
def create_vfat_image(output_file, files_info=None, parameters=None,
parameters_file='parameters.txt', fs_size_kib=100):
"""Creates the fat fs image on the desired file.
@ -299,7 +292,7 @@ def create_esp_image_for_uefi(
# directory.
if deploy_iso and not esp_image:
uefi_path_info, e_img_rel_path, grub_rel_path = (
_mount_deploy_iso(deploy_iso, mountdir))
_get_deploy_iso_files(deploy_iso, mountdir))
grub_cfg = os.path.join(tmpdir, grub_rel_path)
@ -336,7 +329,7 @@ def create_esp_image_for_uefi(
finally:
if deploy_iso:
_umount_without_raise(mountdir)
shutil.rmtree(mountdir)
# Generate and copy grub config file.
grub_conf = _generate_cfg(kernel_params,
@ -622,7 +615,27 @@ def is_whole_disk_image(ctx, instance_info):
return is_whole_disk_image
def _mount_deploy_iso(deploy_iso, mountdir):
def _extract_iso(extract_iso, extract_dir):
# NOTE(rpittau): we could probably just extract the files we need
# if we find them. Also we probably need to detect the correct iso
# type (UDF, RR, JOLIET).
iso = pycdlib.PyCdlib()
iso.open(extract_iso)
for dirname, dirlist, filelist in iso.walk(iso_path='/'):
dir_path = dirname.lstrip('/')
for dir_iso in dirlist:
os.makedirs(os.path.join(extract_dir, dir_path, dir_iso))
for file in filelist:
file_path = os.path.join(extract_dir, dirname, file)
iso.get_file_from_iso(
os.path.join(extract_dir, dir_path, file),
iso_path=file_path)
iso.close()
def _get_deploy_iso_files(deploy_iso, mountdir):
"""This function opens up the deploy iso used for deploy.
:param deploy_iso: path to the deploy iso where its
@ -641,9 +654,9 @@ def _mount_deploy_iso(deploy_iso, mountdir):
grub_path = None
try:
utils.mount(deploy_iso, mountdir, '-o', 'loop')
except processutils.ProcessExecutionError as e:
LOG.exception("mounting the deploy iso failed.")
_extract_iso(deploy_iso, mountdir)
except Exception as e:
LOG.exception("extracting the deploy iso failed.")
raise exception.ImageCreationFailed(image_type='iso', error=e)
try:
@ -658,14 +671,14 @@ def _mount_deploy_iso(deploy_iso, mountdir):
mountdir)
except (OSError, IOError) as e:
LOG.exception("examining the deploy iso failed.")
_umount_without_raise(mountdir)
shutil.rmtree(mountdir)
raise exception.ImageCreationFailed(image_type='iso', error=e)
# check if the variables are assigned some values or not during
# walk of the mountdir.
if not (e_img_path and e_img_rel_path and grub_path and grub_rel_path):
error = (_("Deploy iso didn't contain efiboot.img or grub.cfg"))
_umount_without_raise(mountdir)
shutil.rmtree(mountdir)
raise exception.ImageCreationFailed(image_type='iso', error=error)
uefi_path_info = {e_img_path: e_img_rel_path,

View File

@ -374,13 +374,6 @@ class FsImageTestCase(base.TestCase):
self.assertRaises(exception.ImageCreationFailed,
images.create_vfat_image, 'tgt_file')
@mock.patch.object(utils, 'umount', autospec=True)
def test__umount_without_raise(self, umount_mock):
umount_mock.side_effect = processutils.ProcessExecutionError
images._umount_without_raise('mountdir')
umount_mock.assert_called_once_with('mountdir')
def test__generate_isolinux_cfg(self):
kernel_params = ['key1=value1', 'key2']
@ -414,9 +407,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(self, mount_mock,
walk_mock, relpath_mock):
@mock.patch.object(images, '_extract_iso', autospec=True)
def test__get_deploy_iso_files(self, extract_mock,
walk_mock, relpath_mock):
walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', [], ['grub.cfg']),
('/tmpdir1/isolinux', [],
['efiboot.img', 'isolinux.bin',
@ -424,38 +417,34 @@ class FsImageTestCase(base.TestCase):
relpath_mock.side_effect = ['EFI/ubuntu/grub.cfg',
'isolinux/efiboot.img']
images._mount_deploy_iso('path/to/deployiso', 'tmpdir1')
mount_mock.assert_called_once_with('path/to/deployiso',
'tmpdir1', '-o', 'loop')
images._get_deploy_iso_files('path/to/deployiso', 'tmpdir1')
extract_mock.assert_called_once_with('path/to/deployiso', 'tmpdir1')
walk_mock.assert_called_once_with('tmpdir1')
@mock.patch.object(images, '_umount_without_raise', autospec=True)
@mock.patch.object(shutil, 'rmtree', autospec=True)
@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_esp_imageimg(self, mount_mock,
walk_mock, relpath_mock,
umount_mock):
@mock.patch.object(images, '_extract_iso', autospec=True)
def test__get_deploy_iso_files_fail_no_esp_imageimg(
self, extract_mock, walk_mock, relpath_mock, rmtree_mock):
walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', [], ['grub.cfg']),
('/tmpdir1/isolinux', [],
['isolinux.bin', 'isolinux.cfg'])]
relpath_mock.side_effect = 'EFI/ubuntu/grub.cfg'
self.assertRaises(exception.ImageCreationFailed,
images._mount_deploy_iso,
images._get_deploy_iso_files,
'path/to/deployiso', 'tmpdir1')
mount_mock.assert_called_once_with('path/to/deployiso',
'tmpdir1', '-o', 'loop')
extract_mock.assert_called_once_with('path/to/deployiso', 'tmpdir1')
walk_mock.assert_called_once_with('tmpdir1')
umount_mock.assert_called_once_with('tmpdir1')
rmtree_mock.assert_called_once_with('tmpdir1')
@mock.patch.object(images, '_umount_without_raise', autospec=True)
@mock.patch.object(shutil, 'rmtree', autospec=True)
@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_fails_no_grub_cfg(self, mount_mock,
walk_mock, relpath_mock,
umount_mock):
@mock.patch.object(images, '_extract_iso', autospec=True)
def test__get_deploy_iso_files_fails_no_grub_cfg(
self, extract_mock, walk_mock, relpath_mock, rmtree_mock):
walk_mock.return_value = [('/tmpdir1/EFI/ubuntu', '', []),
('/tmpdir1/isolinux', '',
['efiboot.img', 'isolinux.bin',
@ -463,30 +452,30 @@ class FsImageTestCase(base.TestCase):
relpath_mock.side_effect = 'isolinux/efiboot.img'
self.assertRaises(exception.ImageCreationFailed,
images._mount_deploy_iso,
images._get_deploy_iso_files,
'path/to/deployiso', 'tmpdir1')
mount_mock.assert_called_once_with('path/to/deployiso',
'tmpdir1', '-o', 'loop')
extract_mock.assert_called_once_with('path/to/deployiso', 'tmpdir1')
walk_mock.assert_called_once_with('tmpdir1')
umount_mock.assert_called_once_with('tmpdir1')
rmtree_mock.assert_called_once_with('tmpdir1')
@mock.patch.object(utils, 'mount', autospec=True)
def test__mount_deploy_iso_fail_with_ExecutionError(self, mount_mock):
mount_mock.side_effect = processutils.ProcessExecutionError
def test__get_deploy_iso_files_fail_with_ExecutionError(
self, get_iso_files_mock):
get_iso_files_mock.side_effect = processutils.ProcessExecutionError
self.assertRaises(exception.ImageCreationFailed,
images._mount_deploy_iso,
images._get_deploy_iso_files,
'path/to/deployiso', 'tmpdir1')
@mock.patch.object(images, '_umount_without_raise', autospec=True)
@mock.patch.object(shutil, 'rmtree', autospec=True)
@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(images, '_mount_deploy_iso', autospec=True)
@mock.patch.object(images, '_get_deploy_iso_files', autospec=True)
@mock.patch.object(utils, 'tempdir', autospec=True)
@mock.patch.object(images, '_generate_cfg', autospec=True)
def test_create_esp_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):
self, gen_cfg_mock, tempdir_mock, get_iso_files_mock, execute_mock,
write_to_file_mock, create_root_fs_mock, rmtree_mock):
files_info = {
'path/to/kernel': 'vmlinuz',
@ -513,15 +502,16 @@ class FsImageTestCase(base.TestCase):
mock_file_handle1 = mock.MagicMock(spec=io.BytesIO)
mock_file_handle1.__enter__.return_value = 'mountdir'
tempdir_mock.side_effect = mock_file_handle, mock_file_handle1
mount_mock.return_value = (uefi_path_info,
e_img_rel_path, grub_rel_path)
get_iso_files_mock.return_value = (uefi_path_info,
e_img_rel_path, grub_rel_path)
images.create_esp_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')
get_iso_files_mock.assert_called_once_with('path/to/deploy_iso',
'mountdir')
create_root_fs_mock.assert_called_once_with('tmpdir', files_info)
gen_cfg_mock.assert_any_call(params, CONF.grub_config_template,
grub_options)
@ -529,7 +519,7 @@ class FsImageTestCase(base.TestCase):
execute_mock.assert_called_once_with(
'mkisofs', '-r', '-V', 'VMEDIA_BOOT_ISO', '-l', '-e',
'path/to/efiboot.img', '-no-emul-boot', '-o', 'tgt_file', 'tmpdir')
umount_mock.assert_called_once_with('mountdir')
rmtree_mock.assert_called_once_with('mountdir')
@mock.patch.object(utils, 'write_to_file', autospec=True)
@mock.patch.object(images, '_create_root_fs', autospec=True)
@ -647,14 +637,15 @@ class FsImageTestCase(base.TestCase):
self._test_create_isolinux_image_for_bios(
inject_files={'/source': 'target'})
@mock.patch.object(images, '_umount_without_raise', autospec=True)
@mock.patch.object(images, '_extract_iso', autospec=True)
@mock.patch.object(shutil, 'rmtree', autospec=True)
@mock.patch.object(images, '_create_root_fs', autospec=True)
@mock.patch.object(utils, 'tempdir', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(os, 'walk', autospec=True)
def test_create_esp_image_uefi_rootfs_fails(
self, walk_mock, utils_mock, tempdir_mock,
create_root_fs_mock, umount_mock):
create_root_fs_mock, rmtree_mock, extract_mock):
mock_file_handle = mock.MagicMock(spec=io.BytesIO)
mock_file_handle.__enter__.return_value = 'tmpdir'
@ -669,7 +660,7 @@ class FsImageTestCase(base.TestCase):
'path/to/kernel',
'path/to/ramdisk',
deploy_iso='path/to/deployiso')
umount_mock.assert_called_once_with('mountdir')
rmtree_mock.assert_called_once_with('mountdir')
@mock.patch.object(images, '_create_root_fs', autospec=True)
@mock.patch.object(utils, 'tempdir', autospec=True)
@ -686,22 +677,22 @@ class FsImageTestCase(base.TestCase):
'tgt_file', 'path/to/kernel',
'path/to/ramdisk')
@mock.patch.object(images, '_umount_without_raise', autospec=True)
@mock.patch.object(shutil, 'rmtree', autospec=True)
@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)
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(images, '_mount_deploy_iso', autospec=True)
@mock.patch.object(images, '_get_deploy_iso_files', autospec=True)
@mock.patch.object(images, '_generate_cfg', autospec=True)
def test_create_esp_image_mkisofs_fails(
self, gen_cfg_mock, mount_mock, utils_mock, tempdir_mock,
write_to_file_mock, create_root_fs_mock, umount_mock):
self, gen_cfg_mock, get_iso_files_mock, utils_mock, tempdir_mock,
write_to_file_mock, create_root_fs_mock, rmtree_mock):
mock_file_handle = mock.MagicMock(spec=io.BytesIO)
mock_file_handle.__enter__.return_value = 'tmpdir'
mock_file_handle1 = mock.MagicMock(spec=io.BytesIO)
mock_file_handle1.__enter__.return_value = 'mountdir'
tempdir_mock.side_effect = mock_file_handle, mock_file_handle1
mount_mock.return_value = ({'a': 'a'}, 'b', 'c')
get_iso_files_mock.return_value = ({'a': 'a'}, 'b', 'c')
utils_mock.side_effect = processutils.ProcessExecutionError
self.assertRaises(exception.ImageCreationFailed,
@ -710,7 +701,7 @@ class FsImageTestCase(base.TestCase):
'path/to/kernel',
'path/to/ramdisk',
deploy_iso='path/to/deployiso')
umount_mock.assert_called_once_with('mountdir')
rmtree_mock.assert_called_once_with('mountdir')
@mock.patch.object(images, '_create_root_fs', autospec=True)
@mock.patch.object(utils, 'write_to_file', autospec=True)