summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-09-19 16:48:28 +0000
committerGerrit Code Review <review@openstack.org>2016-09-19 16:48:28 +0000
commitf41c26d4f2452f3c974afe092e1dc6f72955962b (patch)
treea2e7b697c16f528b25da68610833da52f6696bdd
parentb81d59b77d07c018bc79fab1a8c78d67f900ee5a (diff)
parent497be96d4a18e4f2893147ecff6f83d5d64a984e (diff)
downloadironic-f41c26d4f2452f3c974afe092e1dc6f72955962b.tar.gz
Merge "Allow using TempURLs for deploy images"
-rw-r--r--devstack/lib/ironic5
-rw-r--r--etc/ironic/ironic.conf.sample7
-rw-r--r--ironic/conf/pxe.py10
-rw-r--r--ironic/drivers/modules/pxe.py106
-rw-r--r--ironic/tests/unit/drivers/modules/test_pxe.py227
-rw-r--r--releasenotes/notes/ipxe-use-swift-5ccf490daab809cc.yaml12
6 files changed, 240 insertions, 127 deletions
diff --git a/devstack/lib/ironic b/devstack/lib/ironic
index 12aaa5808..ff56411b0 100644
--- a/devstack/lib/ironic
+++ b/devstack/lib/ironic
@@ -212,6 +212,8 @@ IRONIC_HOSTPORT=${IRONIC_HOSTPORT:-$SERVICE_HOST:$IRONIC_SERVICE_PORT}
# Enable iPXE
IRONIC_IPXE_ENABLED=$(trueorfalse True IRONIC_IPXE_ENABLED)
+# Options below are only applied when IRONIC_IPXE_ENABLED is True
+IRONIC_IPXE_USE_SWIFT=$(trueorfalse False IRONIC_IPXE_USE_SWIFT)
IRONIC_HTTP_DIR=${IRONIC_HTTP_DIR:-$IRONIC_DATA_DIR/httpboot}
IRONIC_HTTP_SERVER=${IRONIC_HTTP_SERVER:-$IRONIC_TFTPSERVER_IP}
IRONIC_HTTP_PORT=${IRONIC_HTTP_PORT:-3928}
@@ -743,6 +745,9 @@ function configure_ironic_conductor {
iniset $IRONIC_CONF_FILE pxe pxe_bootfile_name $pxebin
iniset $IRONIC_CONF_FILE deploy http_root $IRONIC_HTTP_DIR
iniset $IRONIC_CONF_FILE deploy http_url "http://$IRONIC_HTTP_SERVER:$IRONIC_HTTP_PORT"
+ if [[ "$IRONIC_IPXE_USE_SWIFT" == "True" ]]; then
+ iniset $IRONIC_CONF_FILE pxe ipxe_use_swift True
+ fi
fi
if [[ "$IRONIC_IS_HARDWARE" == "False" ]]; then
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index 5a2815c0c..0662abfe7 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -2758,6 +2758,13 @@
# Allowed values: 4, 6
#ip_version = 4
+# Download deploy images directly from swift using temporary
+# URLs. If set to false (default), images are downloaded to
+# the ironic-conductor node and served over its local HTTP
+# server. Applicable only when 'ipxe_enabled' option is set to
+# true. (boolean value)
+#ipxe_use_swift = false
+
[seamicro]
diff --git a/ironic/conf/pxe.py b/ironic/conf/pxe.py
index 7e16724ec..e17a919d7 100644
--- a/ironic/conf/pxe.py
+++ b/ironic/conf/pxe.py
@@ -96,6 +96,16 @@ opts = [
choices=['4', '6'],
help=_('The IP version that will be used for PXE booting. '
'Defaults to 4. EXPERIMENTAL')),
+ cfg.BoolOpt('ipxe_use_swift',
+ default=False,
+ help=_("Download deploy images directly from swift using "
+ "temporary URLs. "
+ "If set to false (default), images are downloaded "
+ "to the ironic-conductor node and served over its "
+ "local HTTP server. "
+ "Applicable only when 'ipxe_enabled' option is "
+ "set to true.")),
+
]
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index 7e844c0de..1860a8a43 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -30,6 +30,7 @@ from ironic.common import exception
from ironic.common.glance_service import service_utils
from ironic.common.i18n import _, _LE, _LW
from ironic.common import image_service as service
+from ironic.common import images
from ironic.common import pxe_utils
from ironic.common import states
from ironic.conf import CONF
@@ -81,11 +82,15 @@ def _get_instance_image_info(node, ctx):
:param ctx: context
:returns: a dictionary whose keys are the names of the images (kernel,
ramdisk) and values are the absolute paths of them. If it's a whole
- disk image, it returns an empty dictionary.
+ disk image or node is configured for localboot,
+ it returns an empty dictionary.
"""
image_info = {}
- if node.driver_internal_info.get('is_whole_disk_image'):
- return image_info
+ # NOTE(pas-ha) do not report image kernel and ramdisk for
+ # local boot or whole disk images so that they are not cached
+ if (node.driver_internal_info.get('is_whole_disk_image') or
+ deploy_utils.get_boot_option(node) == 'local'):
+ return image_info
root_dir = pxe_utils.get_root_dir()
i_info = node.instance_info
@@ -125,6 +130,48 @@ def _get_deploy_image_info(node):
return pxe_utils.get_deploy_kr_info(node.uuid, d_info)
+def _get_pxe_kernel_ramdisk(pxe_info):
+ pxe_opts = {}
+ pxe_opts['deployment_aki_path'] = pxe_info['deploy_kernel'][1]
+ pxe_opts['deployment_ari_path'] = pxe_info['deploy_ramdisk'][1]
+ # It is possible that we don't have kernel/ramdisk or even
+ # image_source to determine if it's a whole disk image or not.
+ # For example, when transitioning to 'available' state for first
+ # time from 'manage' state.
+ if 'kernel' in pxe_info:
+ pxe_opts['aki_path'] = pxe_info['kernel'][1]
+ if 'ramdisk' in pxe_info:
+ pxe_opts['ari_path'] = pxe_info['ramdisk'][1]
+ return pxe_opts
+
+
+def _get_ipxe_kernel_ramdisk(task, pxe_info):
+ pxe_opts = {}
+ node = task.node
+
+ for label, option in (('deploy_kernel', 'deployment_aki_path'),
+ ('deploy_ramdisk', 'deployment_ari_path')):
+ image_href = pxe_info[label][0]
+ if (CONF.pxe.ipxe_use_swift and
+ service_utils.is_glance_image(image_href)):
+ pxe_opts[option] = images.get_temp_url_for_glance_image(
+ task.context, image_href)
+ else:
+ pxe_opts[option] = '/'.join([CONF.deploy.http_url, node.uuid,
+ label])
+ # NOTE(pas-ha) do not use Swift TempURLs for kernel and ramdisk
+ # of user image when boot_option is not local,
+ # as this will break instance reboot later when temp urls have timed out.
+ if 'kernel' in pxe_info:
+ pxe_opts['aki_path'] = '/'.join(
+ [CONF.deploy.http_url, node.uuid, 'kernel'])
+ if 'ramdisk' in pxe_info:
+ pxe_opts['ari_path'] = '/'.join(
+ [CONF.deploy.http_url, node.uuid, 'ramdisk'])
+
+ return pxe_opts
+
+
def _build_pxe_config_options(task, pxe_info):
"""Build the PXE config options for a node
@@ -139,47 +186,21 @@ def _build_pxe_config_options(task, pxe_info):
:returns: A dictionary of pxe options to be used in the pxe bootfile
template.
"""
- node = task.node
- is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image')
+ if CONF.pxe.ipxe_enabled:
+ pxe_options = _get_ipxe_kernel_ramdisk(task, pxe_info)
+ else:
+ pxe_options = _get_pxe_kernel_ramdisk(pxe_info)
# These are dummy values to satisfy elilo.
# image and initrd fields in elilo config cannot be blank.
- kernel = 'no_kernel'
- ramdisk = 'no_ramdisk'
+ pxe_options.setdefault('aki_path', 'no_kernel')
+ pxe_options.setdefault('ari_path', 'no_ramdisk')
- if CONF.pxe.ipxe_enabled:
- deploy_kernel = '/'.join([CONF.deploy.http_url, node.uuid,
- 'deploy_kernel'])
- deploy_ramdisk = '/'.join([CONF.deploy.http_url, node.uuid,
- 'deploy_ramdisk'])
- if not is_whole_disk_image:
- kernel = '/'.join([CONF.deploy.http_url, node.uuid,
- 'kernel'])
- ramdisk = '/'.join([CONF.deploy.http_url, node.uuid,
- 'ramdisk'])
- else:
- deploy_kernel = pxe_info['deploy_kernel'][1]
- deploy_ramdisk = pxe_info['deploy_ramdisk'][1]
- if not is_whole_disk_image:
- # It is possible that we don't have kernel/ramdisk or even
- # image_source to determine if it's a whole disk image or not.
- # For example, when transitioning to 'available' state for first
- # time from 'manage' state. Retain dummy values if we don't have
- # kernel/ramdisk.
- if 'kernel' in pxe_info:
- kernel = pxe_info['kernel'][1]
- if 'ramdisk' in pxe_info:
- ramdisk = pxe_info['ramdisk'][1]
-
- pxe_options = {
- 'deployment_aki_path': deploy_kernel,
- 'deployment_ari_path': deploy_ramdisk,
+ pxe_options.update({
'pxe_append_params': CONF.pxe.pxe_append_params,
'tftp_server': CONF.pxe.tftp_server,
- 'aki_path': kernel,
- 'ari_path': ramdisk,
'ipxe_timeout': CONF.pxe.ipxe_timeout * 1000
- }
+ })
return pxe_options
@@ -324,7 +345,8 @@ class PXEBoot(base.BootInterface):
_parse_driver_info(node)
d_info = deploy_utils.get_image_instance_info(node)
- if node.driver_internal_info.get('is_whole_disk_image'):
+ if (node.driver_internal_info.get('is_whole_disk_image') or
+ deploy_utils.get_boot_option(node) == 'local'):
props = []
elif service_utils.is_glance_image(d_info['image_source']):
props = ['kernel_id', 'ramdisk_id']
@@ -386,9 +408,11 @@ class PXEBoot(base.BootInterface):
pxe_config_template)
deploy_utils.try_set_boot_device(task, boot_devices.PXE)
- # FIXME(lucasagomes): If it's local boot we should not cache
- # the image kernel and ramdisk (Or even require it).
- _cache_ramdisk_kernel(task.context, node, pxe_info)
+ if CONF.pxe.ipxe_enabled and CONF.pxe.ipxe_use_swift:
+ pxe_info.pop('deploy_kernel', None)
+ pxe_info.pop('deploy_ramdisk', None)
+ if pxe_info:
+ _cache_ramdisk_kernel(task.context, node, pxe_info)
@METRICS.timer('PXEBoot.clean_up_ramdisk')
def clean_up_ramdisk(self, task):
diff --git a/ironic/tests/unit/drivers/modules/test_pxe.py b/ironic/tests/unit/drivers/modules/test_pxe.py
index 3f314c486..3e67a0341 100644
--- a/ironic/tests/unit/drivers/modules/test_pxe.py
+++ b/ironic/tests/unit/drivers/modules/test_pxe.py
@@ -19,6 +19,7 @@ import filecmp
import os
import shutil
import tempfile
+import uuid
from ironic_lib import utils as ironic_utils
import mock
@@ -145,6 +146,14 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
self.node.save()
self._test__get_instance_image_info()
+ @mock.patch('ironic.drivers.modules.deploy_utils.get_boot_option',
+ return_value='local')
+ def test__get_instance_image_info_localboot(self, boot_opt_mock):
+ self.node.driver_internal_info['is_whole_disk_image'] = False
+ self.node.save()
+ image_info = pxe._get_instance_image_info(self.node, self.context)
+ self.assertEqual({}, image_info)
+
@mock.patch.object(base_image_service.BaseImageService, '_show',
autospec=True)
def test__get_instance_image_info_whole_disk_image(self, show_mock):
@@ -154,11 +163,14 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
image_info = pxe._get_instance_image_info(self.node, self.context)
self.assertEqual({}, image_info)
+ @mock.patch('ironic.common.image_service.GlanceImageService',
+ autospec=True)
@mock.patch.object(pxe_utils, '_build_pxe_config', autospec=True)
- def _test_build_pxe_config_options(self, build_pxe_mock,
+ def _test_build_pxe_config_options(self, build_pxe_mock, glance_mock,
whle_dsk_img=False,
ipxe_enabled=False,
- ipxe_timeout=0):
+ ipxe_timeout=0,
+ ipxe_use_swift=False):
self.config(pxe_append_params='test_param', group='pxe')
# NOTE: right '/' should be removed from url string
self.config(api_url='http://192.168.122.184:6385', group='conductor')
@@ -175,11 +187,18 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
http_url = 'http://192.1.2.3:1234'
self.config(ipxe_enabled=True, group='pxe')
self.config(http_url=http_url, group='deploy')
-
- deploy_kernel = os.path.join(http_url, self.node.uuid,
- 'deploy_kernel')
- deploy_ramdisk = os.path.join(http_url, self.node.uuid,
- 'deploy_ramdisk')
+ if ipxe_use_swift:
+ self.config(ipxe_use_swift=True, group='pxe')
+ glance = mock.Mock()
+ glance_mock.return_value = glance
+ glance.swift_temp_url.side_effect = [
+ deploy_kernel, deploy_ramdisk] = [
+ 'swift_kernel', 'swift_ramdisk']
+ else:
+ deploy_kernel = os.path.join(http_url, self.node.uuid,
+ 'deploy_kernel')
+ deploy_ramdisk = os.path.join(http_url, self.node.uuid,
+ 'deploy_ramdisk')
kernel = os.path.join(http_url, self.node.uuid, 'kernel')
ramdisk = os.path.join(http_url, self.node.uuid, 'ramdisk')
root_dir = CONF.deploy.http_root
@@ -194,9 +213,44 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
'ramdisk')
root_dir = CONF.pxe.tftp_root
- if whle_dsk_img:
- ramdisk = 'no_ramdisk'
- kernel = 'no_kernel'
+ if ipxe_use_swift:
+ image_info = {
+ 'deploy_kernel': (str(uuid.uuid4()),
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'deploy_kernel')),
+ 'deploy_ramdisk': (str(uuid.uuid4()),
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'deploy_ramdisk'))
+ }
+ else:
+ image_info = {
+ 'deploy_kernel': ('deploy_kernel',
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'deploy_kernel')),
+ 'deploy_ramdisk': ('deploy_ramdisk',
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'deploy_ramdisk'))
+ }
+
+ if (whle_dsk_img or
+ deploy_utils.get_boot_option(self.node) == 'local'):
+ ramdisk = 'no_ramdisk'
+ kernel = 'no_kernel'
+ else:
+ image_info.update({
+ 'kernel': ('kernel_id',
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'kernel')),
+ 'ramdisk': ('ramdisk_id',
+ os.path.join(root_dir,
+ self.node.uuid,
+ 'ramdisk'))
+ })
ipxe_timeout_in_ms = ipxe_timeout * 1000
@@ -210,23 +264,6 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
'ipxe_timeout': ipxe_timeout_in_ms,
}
- image_info = {'deploy_kernel': ('deploy_kernel',
- os.path.join(root_dir,
- self.node.uuid,
- 'deploy_kernel')),
- 'deploy_ramdisk': ('deploy_ramdisk',
- os.path.join(root_dir,
- self.node.uuid,
- 'deploy_ramdisk')),
- 'kernel': ('kernel_id',
- os.path.join(root_dir,
- self.node.uuid,
- 'kernel')),
- 'ramdisk': ('ramdisk_id',
- os.path.join(root_dir,
- self.node.uuid,
- 'ramdisk'))}
-
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
options = pxe._build_pxe_config_options(task, image_info)
@@ -236,76 +273,48 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
self._test_build_pxe_config_options(whle_dsk_img=True,
ipxe_enabled=False)
+ def test__build_pxe_config_options_local_boot(self):
+ del self.node.driver_internal_info['is_whole_disk_image']
+ i_info = self.node.instance_info
+ i_info.update({'capabilities': {'boot_option': 'local'}})
+ self.node.instance_info = i_info
+ self.node.save()
+ self._test_build_pxe_config_options(whle_dsk_img=False,
+ ipxe_enabled=False)
+
def test__build_pxe_config_options_ipxe(self):
self._test_build_pxe_config_options(whle_dsk_img=True,
ipxe_enabled=True)
- def test__build_pxe_config_options_without_is_whole_disk_image(self):
+ def test__build_pxe_config_options_ipxe_local_boot(self):
del self.node.driver_internal_info['is_whole_disk_image']
+ i_info = self.node.instance_info
+ i_info.update({'capabilities': {'boot_option': 'local'}})
+ self.node.instance_info = i_info
self.node.save()
self._test_build_pxe_config_options(whle_dsk_img=False,
- ipxe_enabled=False)
+ ipxe_enabled=True)
- def test__build_pxe_config_options_ipxe_and_ipxe_timeout(self):
+ def test__build_pxe_config_options_ipxe_swift_wdi(self):
self._test_build_pxe_config_options(whle_dsk_img=True,
ipxe_enabled=True,
- ipxe_timeout=120)
-
- @mock.patch.object(pxe_utils, '_build_pxe_config', autospec=True)
- def test__build_pxe_config_options_whole_disk_image(self,
- build_pxe_mock,
- ipxe_enabled=False):
- self.config(pxe_append_params='test_param', group='pxe')
- # NOTE: right '/' should be removed from url string
- self.config(api_url='http://192.168.122.184:6385', group='conductor')
+ ipxe_use_swift=True)
- tftp_server = CONF.pxe.tftp_server
-
- if ipxe_enabled:
- http_url = 'http://192.1.2.3:1234'
- self.config(ipxe_enabled=True, group='pxe')
- self.config(http_url=http_url, group='deploy')
-
- deploy_kernel = os.path.join(http_url, self.node.uuid,
- 'deploy_kernel')
- deploy_ramdisk = os.path.join(http_url, self.node.uuid,
- 'deploy_ramdisk')
- root_dir = CONF.deploy.http_root
- else:
- deploy_kernel = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
- 'deploy_kernel')
- deploy_ramdisk = os.path.join(CONF.pxe.tftp_root, self.node.uuid,
- 'deploy_ramdisk')
- root_dir = CONF.pxe.tftp_root
-
- expected_options = {
- 'deployment_ari_path': deploy_ramdisk,
- 'pxe_append_params': 'test_param',
- 'deployment_aki_path': deploy_kernel,
- 'tftp_server': tftp_server,
- 'aki_path': 'no_kernel',
- 'ari_path': 'no_ramdisk',
- 'ipxe_timeout': 0,
- }
+ def test__build_pxe_config_options_ipxe_swift_partition(self):
+ self._test_build_pxe_config_options(whle_dsk_img=False,
+ ipxe_enabled=True,
+ ipxe_use_swift=True)
- image_info = {'deploy_kernel': ('deploy_kernel',
- os.path.join(root_dir,
- self.node.uuid,
- 'deploy_kernel')),
- 'deploy_ramdisk': ('deploy_ramdisk',
- os.path.join(root_dir,
- self.node.uuid,
- 'deploy_ramdisk')),
- }
- driver_internal_info = self.node.driver_internal_info
- driver_internal_info['is_whole_disk_image'] = True
- self.node.driver_internal_info = driver_internal_info
+ def test__build_pxe_config_options_without_is_whole_disk_image(self):
+ del self.node.driver_internal_info['is_whole_disk_image']
self.node.save()
+ self._test_build_pxe_config_options(whle_dsk_img=False,
+ ipxe_enabled=False)
- with task_manager.acquire(self.context, self.node.uuid,
- shared=True) as task:
- options = pxe._build_pxe_config_options(task, image_info)
- self.assertEqual(expected_options, options)
+ def test__build_pxe_config_options_ipxe_and_ipxe_timeout(self):
+ self._test_build_pxe_config_options(whle_dsk_img=True,
+ ipxe_enabled=True,
+ ipxe_timeout=120)
def test__build_pxe_config_options_no_kernel_no_ramdisk(self):
del self.node.driver_internal_info['is_whole_disk_image']
@@ -655,20 +664,38 @@ class PXEBootTestCase(db_base.DbTestCase):
mock_deploy_img_info,
mock_instance_img_info,
dhcp_factory_mock, uefi=False,
- cleaning=False):
+ cleaning=False,
+ ipxe_use_swift=False,
+ whole_disk_image=False):
mock_build_pxe.return_value = {}
mock_deploy_img_info.return_value = {'deploy_kernel': 'a'}
- mock_instance_img_info.return_value = {'kernel': 'b'}
+ if whole_disk_image:
+ mock_instance_img_info.return_value = {}
+ else:
+ mock_instance_img_info.return_value = {'kernel': 'b'}
mock_pxe_config.return_value = None
mock_cache_r_k.return_value = None
provider_mock = mock.MagicMock()
dhcp_factory_mock.return_value = provider_mock
+ driver_internal_info = self.node.driver_internal_info
+ driver_internal_info['is_whole_disk_image'] = whole_disk_image
+ self.node.driver_internal_info = driver_internal_info
+ self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'})
mock_deploy_img_info.assert_called_once_with(task.node)
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
- if cleaning is False:
+ if ipxe_use_swift:
+ if whole_disk_image:
+ self.assertFalse(mock_cache_r_k.called)
+ else:
+ mock_cache_r_k.assert_called_once_with(
+ self.context, task.node,
+ {'kernel': 'b'})
+ mock_instance_img_info.assert_called_once_with(task.node,
+ self.context)
+ elif cleaning is False:
mock_cache_r_k.assert_called_once_with(
self.context, task.node,
{'deploy_kernel': 'a', 'kernel': 'b'})
@@ -749,6 +776,34 @@ class PXEBootTestCase(db_base.DbTestCase):
self._test_prepare_ramdisk()
self.assertFalse(copyfile_mock.called)
+ @mock.patch.object(shutil, 'copyfile', autospec=True)
+ def test_prepare_ramdisk_ipxe_swift(self, copyfile_mock):
+ self.node.provision_state = states.DEPLOYING
+ self.node.save()
+ self.config(group='pxe', ipxe_enabled=True)
+ self.config(group='pxe', ipxe_use_swift=True)
+ self.config(group='deploy', http_url='http://myserver')
+ self._test_prepare_ramdisk(ipxe_use_swift=True)
+ copyfile_mock.assert_called_once_with(
+ CONF.pxe.ipxe_boot_script,
+ os.path.join(
+ CONF.deploy.http_root,
+ os.path.basename(CONF.pxe.ipxe_boot_script)))
+
+ @mock.patch.object(shutil, 'copyfile', autospec=True)
+ def test_prepare_ramdisk_ipxe_swift_whole_disk_image(self, copyfile_mock):
+ self.node.provision_state = states.DEPLOYING
+ self.node.save()
+ self.config(group='pxe', ipxe_enabled=True)
+ self.config(group='pxe', ipxe_use_swift=True)
+ self.config(group='deploy', http_url='http://myserver')
+ self._test_prepare_ramdisk(ipxe_use_swift=True, whole_disk_image=True)
+ copyfile_mock.assert_called_once_with(
+ CONF.pxe.ipxe_boot_script,
+ os.path.join(
+ CONF.deploy.http_root,
+ os.path.basename(CONF.pxe.ipxe_boot_script)))
+
def test_prepare_ramdisk_cleaning(self):
self.node.provision_state = states.CLEANING
self.node.save()
diff --git a/releasenotes/notes/ipxe-use-swift-5ccf490daab809cc.yaml b/releasenotes/notes/ipxe-use-swift-5ccf490daab809cc.yaml
new file mode 100644
index 000000000..dc6fe708d
--- /dev/null
+++ b/releasenotes/notes/ipxe-use-swift-5ccf490daab809cc.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - By default, the ironic-conductor service caches the node's deploy
+ ramdisk and kernel images locally and serves them via a separate
+ HTTP server. A new ``[pxe]ipxe_use_swift`` configuration option
+ (disabled by default) allows images to be accessed directly
+ from object store via Swift temporary URLs.
+ This is only applicable if iPXE is enabled (via ``[pxe]ipxe_enabled``
+ configuration option) and image store is in Glance/Swift.
+ For user images that are partition images requiring non-local boot,
+ the default behavior with local caching and an HTTP server
+ will still apply for user image kernel and ramdisk.