From 1060a570b7345f1b9fd898e08d4629607a44e2d4 Mon Sep 17 00:00:00 2001 From: Julia Kreger Date: Wed, 3 Apr 2024 12:56:57 -0700 Subject: [PATCH] Inject a randomized publisher id To serve as a mechanism to allow an interlocking device identification this patch injects a publisher id value into ISO images *and* the kernel command line for any software running from the ISO image to match the ISO in use to the location of data housed locally from within the image. Some differences exist with this patch due to refactoring changes in I8567a10b77cdc3785686b79defcdafd75af53df0 where the basic flow and logic was simplified just enough to require the logic to change a little bit. Furthermore, even going to 2023.1, some default configuration options were also removed as they were centered around GRUB v1. Related-Bug: 2032377 Change-Id: I9b74ec977fabc0a7f8ed6f113595a3f1624f6ee6 (cherry picked from commit fb850e7f005e0ef4b5c489b8c2b245791d0d33eb) (cherry picked from commit 78c1d9a98d6ff175b3bfa5bdda6694a4cf30cff2) (cherry picked from commit c71e124f9c06fdb5fc0a19a29bc7fb25f71ece9e) --- ironic/common/images.py | 38 ++++-- ironic/drivers/modules/image_utils.py | 34 ++++-- ironic/tests/unit/common/test_images.py | 33 +++-- .../unit/drivers/modules/test_image_utils.py | 115 ++++++++++++------ ...blisher-id-injection-c88674a31634f852.yaml | 6 + 5 files changed, 164 insertions(+), 62 deletions(-) create mode 100644 releasenotes/notes/virtual-media-publisher-id-injection-c88674a31634f852.yaml diff --git a/ironic/common/images.py b/ironic/common/images.py index aa883ada3b..8e702cd288 100644 --- a/ironic/common/images.py +++ b/ironic/common/images.py @@ -175,7 +175,8 @@ def _label(files_info): def create_isolinux_image_for_bios( - output_file, kernel, ramdisk, kernel_params=None, inject_files=None): + output_file, kernel, ramdisk, kernel_params=None, inject_files=None, + publisher_id=None): """Creates an isolinux image on the specified file. Copies the provided kernel, ramdisk to a directory, generates the isolinux @@ -191,6 +192,8 @@ def create_isolinux_image_for_bios( as the kernel cmdline. :param inject_files: Mapping of local source file paths to their location on the final ISO image. + :param publisher_id: A value to set as the publisher identifier string + in the ISO image to be generated. :raises: ImageCreationFailed, if image creation failed while copying files or while running command to generate iso. """ @@ -237,9 +240,12 @@ def create_isolinux_image_for_bios( isolinux_cfg = os.path.join(tmpdir, ISOLINUX_CFG) utils.write_to_file(isolinux_cfg, cfg) + # Set a publisher ID value to a string. + pub_id = str(publisher_id) + try: utils.execute('mkisofs', '-r', '-V', _label(files_info), - '-J', '-l', '-no-emul-boot', + '-J', '-l', '-publisher', pub_id, '-no-emul-boot', '-boot-load-size', '4', '-boot-info-table', '-b', ISOLINUX_BIN, '-o', output_file, tmpdir) except processutils.ProcessExecutionError as e: @@ -249,7 +255,7 @@ def create_isolinux_image_for_bios( def create_esp_image_for_uefi( output_file, kernel, ramdisk, deploy_iso=None, esp_image=None, - kernel_params=None, inject_files=None): + kernel_params=None, inject_files=None, publisher_id=None): """Creates an ESP image on the specified file. Copies the provided kernel, ramdisk and EFI system partition image (ESP) to @@ -271,6 +277,8 @@ def create_esp_image_for_uefi( as the kernel cmdline. :param inject_files: Mapping of local source file paths to their location on the final ISO image. + :param publisher_id: A value to set as the publisher identifier string + in the ISO image to be generated. :raises: ImageCreationFailed, if image creation failed while copying files or while running command to generate iso. """ @@ -337,10 +345,18 @@ def create_esp_image_for_uefi( utils.write_to_file(grub_cfg, grub_conf) # Create the boot_iso. + if publisher_id: + args = ('mkisofs', '-r', '-V', _label(files_info), + '-l', '-publisher', publisher_id, '-e', e_img_rel_path, + '-no-emul-boot', '-o', output_file, + tmpdir) + else: + args = ('mkisofs', '-r', '-V', _label(files_info), + '-l', '-e', e_img_rel_path, + '-no-emul-boot', '-o', output_file, + tmpdir) try: - utils.execute('mkisofs', '-r', '-V', _label(files_info), - '-l', '-e', e_img_rel_path, '-no-emul-boot', - '-o', output_file, tmpdir) + utils.execute(*args) except processutils.ProcessExecutionError as e: LOG.exception("Creating ISO image failed.") @@ -498,7 +514,7 @@ def get_temp_url_for_glance_image(context, image_uuid): def create_boot_iso(context, output_filename, kernel_href, ramdisk_href, deploy_iso_href=None, esp_image_href=None, root_uuid=None, kernel_params=None, boot_mode=None, - inject_files=None): + inject_files=None, publisher_id=None): """Creates a bootable ISO image for a node. Given the hrefs for kernel, ramdisk, root partition's UUID and @@ -524,6 +540,8 @@ def create_boot_iso(context, output_filename, kernel_href, :boot_mode: the boot mode in which the deploy is to happen. :param inject_files: Mapping of local source file paths to their location on the final ISO image. + :param publisher_id: A value to set as the publisher identifier string + in the ISO image to be generated. :raises: ImageCreationFailed, if creating boot ISO failed. """ with utils.tempdir() as tmpdir: @@ -560,12 +578,14 @@ def create_boot_iso(context, output_filename, kernel_href, create_esp_image_for_uefi( output_filename, kernel_path, ramdisk_path, deploy_iso=deploy_iso_path, esp_image=esp_image_path, - kernel_params=params, inject_files=inject_files) + kernel_params=params, inject_files=inject_files, + publisher_id=publisher_id) else: create_isolinux_image_for_bios( output_filename, kernel_path, ramdisk_path, - kernel_params=params, inject_files=inject_files) + kernel_params=params, inject_files=inject_files, + publisher_id=publisher_id) IMAGE_TYPE_PARTITION = 'partition' diff --git a/ironic/drivers/modules/image_utils.py b/ironic/drivers/modules/image_utils.py index 6de887d394..92504b1eb2 100644 --- a/ironic/drivers/modules/image_utils.py +++ b/ironic/drivers/modules/image_utils.py @@ -24,6 +24,7 @@ from urllib import parse as urlparse from ironic_lib import utils as ironic_utils from oslo_log import log +from oslo_utils import uuidutils from ironic.common import exception from ironic.common.glance_service import service_utils @@ -494,6 +495,10 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, img_handler = ImageHandler(task.node.driver) boot_mode = boot_mode_utils.get_boot_mode(task.node) + if not is_ramdisk_boot: + publisher_id = uuidutils.generate_uuid() + else: + publisher_id = None with tempfile.TemporaryDirectory(dir=CONF.tempdir) as boot_file_dir: @@ -516,6 +521,7 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, else: kernel_params = driver_utils.get_kernel_append_params( task.node, default=img_handler.kernel_params) + kernel_params += " ir_pub_id=%s" % publisher_id if params: kernel_params = ' '.join( @@ -533,14 +539,26 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, 'ramdisk_href': ramdisk_href, 'bootloader_href': bootloader_href, 'params': kernel_params}) - images.create_boot_iso( - task.context, boot_iso_tmp_file, - kernel_href, ramdisk_href, - esp_image_href=bootloader_href, - root_uuid=root_uuid, - kernel_params=kernel_params, - boot_mode=boot_mode, - inject_files=inject_files) + + if publisher_id: + images.create_boot_iso( + task.context, boot_iso_tmp_file, + kernel_href, ramdisk_href, + esp_image_href=bootloader_href, + root_uuid=root_uuid, + kernel_params=kernel_params, + boot_mode=boot_mode, + inject_files=inject_files, + publisher_id=publisher_id) + else: + images.create_boot_iso( + task.context, boot_iso_tmp_file, + kernel_href, ramdisk_href, + esp_image_href=bootloader_href, + root_uuid=root_uuid, + kernel_params=kernel_params, + boot_mode=boot_mode, + inject_files=inject_files) iso_object_name = _get_name(task.node, prefix='boot', suffix='.iso') diff --git a/ironic/tests/unit/common/test_images.py b/ironic/tests/unit/common/test_images.py index 5530feb38c..129235669e 100644 --- a/ironic/tests/unit/common/test_images.py +++ b/ironic/tests/unit/common/test_images.py @@ -570,7 +570,8 @@ class FsImageTestCase(base.TestCase): 'path/to/kernel', 'path/to/ramdisk', deploy_iso='path/to/deploy_iso', - kernel_params=params) + kernel_params=params, + publisher_id='1-23-4') get_iso_files_mock.assert_called_once_with('path/to/deploy_iso', 'mountdir') create_root_fs_mock.assert_called_once_with('tmpdir', files_info) @@ -578,7 +579,8 @@ class FsImageTestCase(base.TestCase): 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', + 'mkisofs', '-r', '-V', 'VMEDIA_BOOT_ISO', '-l', + '-publisher', '1-23-4', '-e', 'path/to/efiboot.img', '-no-emul-boot', '-o', 'tgt_file', 'tmpdir') rmtree_mock.assert_called_once_with('mountdir') @@ -653,7 +655,8 @@ class FsImageTestCase(base.TestCase): 'path/to/kernel', 'path/to/ramdisk', kernel_params=params, - inject_files=inject_files) + inject_files=inject_files, + publisher_id='1-23-4') files_info = { 'path/to/kernel': 'vmlinuz', @@ -672,6 +675,7 @@ class FsImageTestCase(base.TestCase): execute_mock.assert_called_once_with( 'mkisofs', '-r', '-V', "VMEDIA_BOOT_ISO", '-J', '-l', + '-publisher', '1-23-4', '-no-emul-boot', '-boot-load-size', '4', '-boot-info-table', '-b', 'isolinux/isolinux.bin', '-o', 'tgt_file', 'tmpdir') @@ -811,7 +815,8 @@ class FsImageTestCase(base.TestCase): create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', deploy_iso='tmpdir/iso', - esp_image=None, kernel_params=params, inject_files=None) + esp_image=None, kernel_params=params, inject_files=None, + publisher_id=None) @mock.patch.object(images, 'create_esp_image_for_uefi', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -839,7 +844,8 @@ class FsImageTestCase(base.TestCase): create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', deploy_iso=None, esp_image='tmpdir/esp', - kernel_params=params, inject_files=None) + kernel_params=params, inject_files=None, + publisher_id=None) @mock.patch.object(images, 'create_esp_image_for_uefi', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -867,7 +873,8 @@ class FsImageTestCase(base.TestCase): create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', deploy_iso='tmpdir/iso', - esp_image=None, kernel_params=params, inject_files=None) + esp_image=None, kernel_params=params, inject_files=None, + publisher_id=None) @mock.patch.object(images, 'create_esp_image_for_uefi', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -882,7 +889,7 @@ class FsImageTestCase(base.TestCase): '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') + boot_mode='uefi', publisher_id='1-23-4') expected_calls = [mock.call('ctx', 'http://kernel-href', 'tmpdir/kernel'), @@ -895,7 +902,8 @@ class FsImageTestCase(base.TestCase): create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', deploy_iso=None, esp_image='tmpdir/esp', - kernel_params=params, inject_files=None) + kernel_params=params, inject_files=None, + publisher_id='1-23-4') @mock.patch.object(images, 'create_isolinux_image_for_bios', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -909,7 +917,8 @@ class FsImageTestCase(base.TestCase): images.create_boot_iso('ctx', 'output_file', 'kernel-uuid', 'ramdisk-uuid', 'deploy_iso-uuid', 'efiboot-uuid', 'root-uuid', - 'kernel-params', 'bios') + 'kernel-params', 'bios', + publisher_id='1-23-4') fetch_images_mock.assert_any_call( 'ctx', 'kernel-uuid', 'tmpdir/kernel') @@ -926,7 +935,8 @@ class FsImageTestCase(base.TestCase): params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', - kernel_params=params, inject_files=None) + kernel_params=params, inject_files=None, + publisher_id='1-23-4') @mock.patch.object(images, 'create_isolinux_image_for_bios', autospec=True) @mock.patch.object(images, 'fetch', autospec=True) @@ -951,7 +961,8 @@ class FsImageTestCase(base.TestCase): params = ['root=UUID=root-uuid', 'kernel-params'] create_isolinux_mock.assert_called_once_with( 'output_file', 'tmpdir/kernel', 'tmpdir/ramdisk', - kernel_params=params, inject_files=None) + kernel_params=params, inject_files=None, + publisher_id=None) @mock.patch.object(image_service, 'get_image_service', autospec=True) def test_get_glance_image_properties_no_such_prop(self, diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py index ccf568f381..858e0f0e45 100644 --- a/ironic/tests/unit/drivers/modules/test_image_utils.py +++ b/ironic/tests/unit/drivers/modules/test_image_utils.py @@ -558,11 +558,14 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): mock_unpublish.assert_called_once_with(mock.ANY, object_name) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_uefi( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: task.node.instance_info.update(deploy_boot_mode='uefi') @@ -583,18 +586,21 @@ 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='uefi', esp_image_href='http://bootloader/img', - kernel_params='nofb nomodeset vga=normal', + kernel_params='nofb nomodeset vga=normal ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') self.assertEqual(expected_url, url) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_default_boot_mode( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): self.config(default_boot_mode='uefi', group='deploy') + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: image_utils._prepare_iso_image( @@ -604,16 +610,19 @@ 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='uefi', esp_image_href=None, - kernel_params='nofb nomodeset vga=normal', + kernel_params='nofb nomodeset vga=normal ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_with_node_external_http_url( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): self.config(default_boot_mode='uefi', group='deploy') + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: override_url = 'https://node.external/' @@ -634,18 +643,21 @@ 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='uefi', esp_image_href='http://bootloader/img', - kernel_params=mock.ANY, + kernel_params='nofb nomodeset vga=normal ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') self.assertEqual(expected_url, url) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_bios( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): self.config(default_boot_mode='bios', group='deploy') + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: @@ -665,17 +677,20 @@ 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, - kernel_params='nofb nomodeset vga=normal', + kernel_params='nofb nomodeset vga=normal ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') self.assertEqual(expected_url, url) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -689,15 +704,19 @@ 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='uefi', esp_image_href=None, - kernel_params=kernel_params, + kernel_params=f'{kernel_params} ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -711,15 +730,19 @@ 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='uefi', esp_image_href=None, - kernel_params=kernel_params, + kernel_params=f'{kernel_params} ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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_defaults( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -734,16 +757,21 @@ 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='uefi', esp_image_href=None, - kernel_params=f'nofb nomodeset vga=normal {kernel_params}', + kernel_params=(f'nofb nomodeset vga=normal {kernel_params} ' + 'ir_pub_id=1-23-4'), root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): self.config(default_boot_mode='bios', group='deploy') + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -757,16 +785,20 @@ 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, - kernel_params=kernel_params, + kernel_params=f'{kernel_params} ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, + publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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_uefi( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -780,17 +812,21 @@ 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='uefi', esp_image_href=None, - kernel_params="root=/dev/ram0 text " + kernel_params, + kernel_params=f'root=/dev/ram0 text {kernel_params}', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', inject_files=None) + mock_generate_uuid.assert_not_called() + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): self.config(default_boot_mode='bios', group='deploy') + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -804,16 +840,20 @@ 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, - kernel_params="root=/dev/ram0 text " + kernel_params, + kernel_params=f'root=/dev/ram0 text {kernel_params}', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', inject_files=None) + mock_generate_uuid.assert_not_called() + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @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_cleaning( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -828,15 +868,19 @@ 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='uefi', esp_image_href=None, - kernel_params=kernel_params, + kernel_params=f'{kernel_params} ir_pub_id=1-23-4', root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + @mock.patch.object(uuidutils, 'generate_uuid', autospec=True) @mock.patch.object(image_utils.ImageHandler, 'publish_image', autospec=True) @mock.patch.object(images, 'create_boot_iso', autospec=True) def test__prepare_iso_image_extra_params( - self, mock_create_boot_iso, mock_publish_image): + self, mock_create_boot_iso, mock_publish_image, + mock_generate_uuid): + mock_generate_uuid.return_value = '1-23-4' with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: kernel_params = 'network-config=base64-cloudinit-blob' @@ -851,9 +895,12 @@ 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='uefi', esp_image_href=None, - kernel_params=kernel_params + ' foo=bar banana', + kernel_params=(f'{kernel_params} ir_pub_id=1-23-4 ' + 'foo=bar banana'), root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', - inject_files=None) + inject_files=None, publisher_id='1-23-4') + self.assertEqual(1, mock_generate_uuid.call_count) + self.assertEqual(1, mock_generate_uuid.call_count) def test__prepare_iso_image_bootable_iso(self): with task_manager.acquire(self.context, self.node.uuid, diff --git a/releasenotes/notes/virtual-media-publisher-id-injection-c88674a31634f852.yaml b/releasenotes/notes/virtual-media-publisher-id-injection-c88674a31634f852.yaml new file mode 100644 index 0000000000..ea05b3e350 --- /dev/null +++ b/releasenotes/notes/virtual-media-publisher-id-injection-c88674a31634f852.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Adds an ISO publisher value to ISO images which are mastered as part of + cleaning/deployment/service operations in support of a fix for + `bug 2032377 `_.