Use an ImageCache for provided boot/deploy ISO images

When pre-built images are used, they can be quite large (CoreOS is more
than 700 MiB, the same probably applies to other live ISOs). When
deploying many nodes at the same time, the disk usage may become really
large.

This change uses the same ImageCache as for instance images for ISO
images as well.

Story: #2008909
Task: #42497
Change-Id: I891fd484f98042039895d82046199e8b29975100
This commit is contained in:
Dmitry Tantsur 2021-09-08 14:38:24 +02:00
parent c80d2f2957
commit 93e57fd727
5 changed files with 57 additions and 16 deletions

View File

@ -92,14 +92,6 @@ For example,
--instance-info boot_iso=http://path/to/boot.iso
baremetal node deploy <NODE>
By default the Bare Metal service will cache the ISO locally and serve from its
HTTP server. If you want to avoid that, set the following:
.. code-block:: shell
baremetal node set <NODE> \
--instance-info ramdisk_image_download_source=http
.. warning::
This feature, when utilized with the ``ipxe`` ``boot_interface``,
will only allow a kernel and ramdisk to be booted from the
@ -113,6 +105,18 @@ HTTP server. If you want to avoid that, set the following:
This is a limitation of iPXE and the overall boot process of the
operating system where memory allocated by iPXE is released.
By default the Bare Metal service will cache the ISO locally and serve from its
HTTP server. If you want to avoid that, set the following:
.. code-block:: shell
baremetal node set <NODE> \
--instance-info ramdisk_image_download_source=http
ISO images are also cached across deployments, similarly to how it is done for
normal instance images. The URL together with the last modified response header
are used to determine if an image needs updating.
Limitations
-----------

View File

@ -212,6 +212,21 @@ opts = [
'url directly, or if ironic should cache the image on '
'the conductor and serve it from ironic\'s own http '
'server.')),
cfg.StrOpt('iso_master_path',
default='/var/lib/ironic/master_iso_images',
help=_('On the ironic-conductor node, directory where master '
'ISO images are stored on disk. '
'Setting to the empty string disables image caching.')),
cfg.IntOpt('iso_cache_size',
default=20480,
help=_('Maximum size (in MiB) of cache for master ISO images, '
'including those in use.')),
# 10080 here is 1 week - 60*24*7. It is entirely arbitrary in the absence
# of a facility to disable the ttl entirely.
cfg.IntOpt('iso_cache_ttl',
default=10080,
help=_('Maximum TTL (in minutes) for old master ISO images in '
'cache.')),
]

View File

@ -35,6 +35,7 @@ from ironic.common import utils
from ironic.conf import CONF
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules import image_cache
from ironic.drivers import utils as driver_utils
LOG = log.getLogger(__name__)
@ -226,6 +227,19 @@ class ImageHandler(object):
return image_url
@image_cache.cleanup(priority=75)
class ISOImageCache(image_cache.ImageCache):
def __init__(self):
master_path = CONF.deploy.iso_master_path or None
super(self.__class__, self).__init__(
master_path,
# MiB -> B
cache_size=CONF.deploy.iso_cache_size * 1024 * 1024,
# min -> sec
cache_ttl=CONF.deploy.iso_cache_ttl * 60)
def _get_name(node, prefix='', suffix=''):
"""Get an object name for a given node.
@ -467,10 +481,9 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href,
boot_mode = boot_mode_utils.get_boot_mode(task.node)
with tempfile.NamedTemporaryFile(
dir=CONF.tempdir, suffix='.iso') as boot_fileobj:
with tempfile.TemporaryDirectory(dir=CONF.tempdir) as boot_file_dir:
boot_iso_tmp_file = boot_fileobj.name
boot_iso_tmp_file = os.path.join(boot_file_dir, 'boot.iso')
if base_iso:
# NOTE(dtantsur): this should be "params or inject_files", but
# params are always populated in the calling code.
@ -479,7 +492,9 @@ def _prepare_iso_image(task, kernel_href, ramdisk_href,
'%(node)s, custom configuration will not be available',
{'boot_mode': boot_mode, 'node': task.node.uuid,
'iso': base_iso})
images.fetch_into(task.context, base_iso, boot_iso_tmp_file)
ISOImageCache().fetch_image(base_iso, boot_iso_tmp_file,
ctx=task.context, force_raw=False)
else:
if is_ramdisk_boot:
kernel_params = "root=/dev/ram0 text "

View File

@ -671,10 +671,10 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
@mock.patch.object(image_utils.ImageHandler, 'publish_image',
autospec=True)
@mock.patch.object(images, 'fetch_into', autospec=True)
@mock.patch.object(image_utils, 'ISOImageCache', 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_fetch,
mock_cache,
mock_publish_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
@ -684,8 +684,8 @@ class RedfishImageUtilsTestCase(db_base.DbTestCase):
task, 'http://kernel/img', 'http://ramdisk/img',
bootloader_href=None, root_uuid=task.node.uuid,
base_iso=base_image_url)
mock_fetch.assert_called_once_with(task.context,
base_image_url, mock.ANY)
mock_cache.return_value.fetch_image.assert_called_once_with(
base_image_url, mock.ANY, ctx=task.context, force_raw=False)
mock_create_boot_iso.assert_not_called()
@mock.patch.object(images, 'get_temp_url_for_glance_image',

View File

@ -0,0 +1,7 @@
---
features:
- |
ISO images provided via ``instance_info/boot_iso`` or
`instance_info/deploy_iso`` are now cached in a similar way to normal
instance images. Set ``[deploy]iso_master_path`` to an empty string
to disable.