diff --git a/ironic/conf/deploy.py b/ironic/conf/deploy.py index 69d5fa537d..1e63c9a868 100644 --- a/ironic/conf/deploy.py +++ b/ironic/conf/deploy.py @@ -193,6 +193,22 @@ opts = [ 'cleaning operations to fail due to the possible ' 'operational security risk of data being retained ' 'between deployments of the bare metal node.')), + cfg.StrOpt('ramdisk_image_download_source', + choices=[('http', _('In case the ramdisk is already a bootable ' + 'iso, using this option it will be ' + 'directly provided by an external HTTP ' + 'service using its full url.')), + ('local', _('This is the default behavior. ' + 'The image is downloaded, prepared and ' + 'cached locally, to be served from ' + 'the conductor.'))], + default='local', + mutable=True, + help=_('Specifies whether a boot iso image should be served ' + 'from its own original location using the image source ' + 'url directly, or if ironic should cache the image on ' + 'the conductor and serve it from ironic\'s own http ' + 'server.')), ] diff --git a/ironic/drivers/modules/image_utils.py b/ironic/drivers/modules/image_utils.py index f1283b092a..37c59ecc34 100644 --- a/ironic/drivers/modules/image_utils.py +++ b/ironic/drivers/modules/image_utils.py @@ -404,7 +404,7 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, :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 bootloader_href: URL or Glance UUID of the EFI bootloader - image to use when creating UEFI bootbable ISO + image to use when creating UEFI bootable ISO :param root_uuid: optional uuid of the root partition. :param params: a dictionary containing 'parameter name'->'value' mapping to be passed to kernel command line. @@ -423,6 +423,15 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href, "building ISO, or explicit ISO for %(node)s") % {'node': task.node.uuid}) + # NOTE(rpittau): if base_iso is defined as http address, we just access + # it directly. + if base_iso and CONF.deploy.ramdisk_image_download_source == 'http': + if base_iso.startswith(('http://', 'https://')): + return base_iso + LOG.debug("ramdisk_image_download_source set to http but " + "boot_iso is not an HTTP URL: %(boot_iso)s", + {"boot_iso": base_iso}) + img_handler = ImageHandler(task.node.driver) k_param = img_handler.kernel_params diff --git a/ironic/tests/unit/drivers/modules/test_image_utils.py b/ironic/tests/unit/drivers/modules/test_image_utils.py index cb41db3eb0..c527c53dcb 100644 --- a/ironic/tests/unit/drivers/modules/test_image_utils.py +++ b/ironic/tests/unit/drivers/modules/test_image_utils.py @@ -553,6 +553,36 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase): root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', base_iso='/path/to/baseiso', inject_files=None) + def test__prepare_iso_image_bootable_iso(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + base_image_url = 'http://bearmetal.net/boot.iso' + self.config(ramdisk_image_download_source='http', group='deploy') + url = image_utils._prepare_iso_image( + task, None, None, bootloader_href=None, root_uuid=None, + base_iso=base_image_url) + self.assertEqual(url, base_image_url) + + @mock.patch.object(image_utils.ImageHandler, 'publish_image', + autospec=True) + @mock.patch.object(images, 'create_boot_iso', autospec=True) + def test__prepare_iso_image_bootable_iso_file(self, mock_create_boot_iso, + mock_publish_image): + with task_manager.acquire(self.context, self.node.uuid, + shared=True) as task: + base_image_url = '/path/to/baseiso' + self.config(ramdisk_image_download_source='http', group='deploy') + image_utils._prepare_iso_image( + task, 'http://kernel/img', 'http://ramdisk/img', + bootloader_href=None, root_uuid=task.node.uuid, + base_iso=base_image_url) + mock_create_boot_iso.assert_called_once_with( + mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img', + esp_image_href=None, + root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123', + kernel_params='nofb nomodeset vga=normal', boot_mode='bios', + base_iso='/path/to/baseiso', inject_files=None) + def test__find_param(self): param_dict = { 'ilo_deploy_kernel': 'kernel', diff --git a/releasenotes/notes/option-no-cache-http-ramdisk-62fc29cdd1d5b152.yaml b/releasenotes/notes/option-no-cache-http-ramdisk-62fc29cdd1d5b152.yaml new file mode 100644 index 0000000000..2e1519b154 --- /dev/null +++ b/releasenotes/notes/option-no-cache-http-ramdisk-62fc29cdd1d5b152.yaml @@ -0,0 +1,14 @@ +--- +fixes: + - | + The behavior when a bootable iso ramdisk is provided behind an http server + is to download and serve the image from the conductor; the image is + removed only when the node is undeployed. + In certain cases, for example on large deployments, this could cause + undesired behaviors, like the conductor nodes running out of disk + storage. + To avoid this event we provide an option + ``[deploy]ramdisk_image_download_source`` to be able to tell the ramdisk + interface to directly use the bootable iso url from its original source + instead of downloading it and serving it from the conductor node. + The default behavior is unchanged.