From fb850e7f005e0ef4b5c489b8c2b245791d0d33eb 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. Related-Bug: 2032377 Change-Id: I9b74ec977fabc0a7f8ed6f113595a3f1624f6ee6 --- ironic/common/images.py | 38 ++++-- ironic/drivers/modules/image_utils.py | 36 ++++-- 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, 166 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 b465e6c583..9947927bc1 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 021cad8508..c74d6ccc88 100644 --- a/ironic/drivers/modules/image_utils.py +++ b/ironic/drivers/modules/image_utils.py @@ -23,6 +23,7 @@ import tempfile from urllib import parse as urlparse from oslo_log import log +from oslo_utils import uuidutils from ironic.common import exception from ironic.common.glance_service import service_utils @@ -446,6 +447,9 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, img_handler = ImageHandler(task.node.driver) + if not is_ramdisk_boot: + publisher_id = uuidutils.generate_uuid() + with tempfile.TemporaryDirectory(dir=CONF.tempdir) as boot_file_dir: boot_iso_tmp_file = os.path.join(boot_file_dir, 'boot.iso') @@ -456,6 +460,9 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, kernel_params = driver_utils.get_kernel_append_params( task.node, default=img_handler.kernel_params) + if not is_ramdisk_boot: + kernel_params += " ir_pub_id=%s" % publisher_id + if params: kernel_params = ' '.join( (kernel_params, ' '.join( @@ -472,14 +479,27 @@ 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 is_ramdisk_boot: + 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) + + 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, + publisher_id=publisher_id) node_http_url = task.node.driver_info.get("external_http_url") image_url = img_handler.publish_image( diff --git a/ironic/tests/unit/common/test_images.py b/ironic/tests/unit/common/test_images.py index d73c57e524..e0965c3bf8 100644 --- a/ironic/tests/unit/common/test_images.py +++ b/ironic/tests/unit/common/test_images.py @@ -568,7 +568,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) @@ -576,7 +577,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') @@ -651,7 +653,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', @@ -670,6 +673,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') @@ -809,7 +813,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) @@ -837,7 +842,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) @@ -865,7 +871,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) @@ -880,7 +887,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'), @@ -893,7 +900,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) @@ -907,7 +915,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') @@ -924,7 +933,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) @@ -949,7 +959,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 8ea877976b..ee0ebb519d 100644 --- a/ironic/tests/unit/drivers/modules/test_image_utils.py +++ b/ironic/tests/unit/drivers/modules/test_image_utils.py @@ -379,11 +379,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') @@ -404,18 +407,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 vga=normal', + kernel_params='nofb 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( @@ -425,16 +431,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 vga=normal', + kernel_params='nofb 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/' @@ -455,18 +464,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 vga=normal', + kernel_params='nofb 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: @@ -486,17 +498,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 vga=normal', + kernel_params='nofb 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' @@ -510,15 +525,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' @@ -532,15 +551,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' @@ -555,16 +578,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 vga=normal {kernel_params}', + kernel_params=(f'nofb 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' @@ -578,16 +606,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' @@ -601,17 +633,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' @@ -625,16 +661,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' @@ -649,15 +689,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' @@ -672,9 +716,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) @mock.patch.object(images, 'create_boot_iso', autospec=True) @mock.patch.object(image_utils, 'prepare_remote_image', autospec=True) 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 `_.