summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-10-25 06:29:00 +0000
committerGerrit Code Review <review@openstack.org>2018-10-25 06:29:00 +0000
commitdd02698ad41774513aee0b123158c003e4af76eb (patch)
tree3d71541c2e5677f58553f15d29e40a209ba45cf5
parent7140ee207dd2d2932c2542d26cd77bd05de4b103 (diff)
parent90d58ede94daa439aa784240b8157bdf8039ee37 (diff)
downloadironic-dd02698ad41774513aee0b123158c003e4af76eb.tar.gz
Merge "Fix DHCPv6 support"
-rw-r--r--ironic/common/pxe_utils.py95
-rw-r--r--ironic/drivers/modules/ipxe.py6
-rw-r--r--ironic/drivers/modules/pxe.py10
-rw-r--r--ironic/tests/unit/common/test_pxe_utils.py49
-rw-r--r--ironic/tests/unit/drivers/modules/test_ipxe.py18
-rw-r--r--ironic/tests/unit/drivers/modules/test_pxe.py24
-rw-r--r--releasenotes/notes/boot-from-url-98d21670e726c518.yaml9
7 files changed, 151 insertions, 60 deletions
diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py
index 37e6ffd25..9aca80d8d 100644
--- a/ironic/common/pxe_utils.py
+++ b/ironic/common/pxe_utils.py
@@ -43,9 +43,15 @@ PXE_CFG_DIR_NAME = CONF.pxe.pxe_config_subdir
DHCP_CLIENT_ID = '61' # rfc2132
DHCP_TFTP_SERVER_NAME = '66' # rfc2132
DHCP_BOOTFILE_NAME = '67' # rfc2132
+DHCPV6_BOOTFILE_NAME = '59' # rfc5870
+# NOTE(TheJulia): adding note for the bootfile parameter
+# field as defined by RFC 5870. No practical examples seem
+# available. Neither grub2 or ipxe seem to leverage this.
+# DHCPV6_BOOTFILE_PARAMS = '60' # rfc5870
DHCP_TFTP_SERVER_ADDRESS = '150' # rfc5859
DHCP_IPXE_ENCAP_OPTS = '175' # Tentatively Assigned
DHCP_TFTP_PATH_PREFIX = '210' # rfc5071
+
DEPLOY_KERNEL_RAMDISK_LABELS = ['deploy_kernel', 'deploy_ramdisk']
RESCUE_KERNEL_RAMDISK_LABELS = ['rescue_kernel', 'rescue_ramdisk']
KERNEL_RAMDISK_LABELS = {'deploy': DEPLOY_KERNEL_RAMDISK_LABELS,
@@ -391,16 +397,58 @@ def clean_up_pxe_config(task):
task.node.uuid))
-def dhcp_options_for_instance(task):
+def _dhcp_option_file_or_url(task, urlboot=False):
+ """Returns the appropriate file or URL.
+
+ :param task: A TaskManager object.
+ :param url_boot: Boolean value default False to indicate if a
+ URL should be returned to the file as opposed
+ to a file.
+ """
+ boot_file = deploy_utils.get_pxe_boot_file(task.node)
+ # NOTE(TheJulia): There are additional cases as we add new
+ # features, so the logic below is in the form of if/elif/elif
+ if not urlboot:
+ return boot_file
+ elif urlboot:
+ path_prefix = get_tftp_path_prefix()
+ if path_prefix == '':
+ path_prefix = '/'
+ return ("tftp://" + CONF.pxe.tftp_server
+ + path_prefix + boot_file)
+
+
+def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
"""Retrieves the DHCP PXE boot options.
:param task: A TaskManager instance.
+ :param ipxe_enabled: Default false boolean that siganls if iPXE
+ formatting should be returned by the method
+ for DHCP server configuration.
+ :param url_boot: Default false boolean to inform the method if
+ a URL should be returned to boot the node.
+ If [pxe]ip_version is set to `6`, then this option
+ has no effect as url_boot form is required by DHCPv6
+ standards.
+ :returns: Dictionary to be sent to the networking service describing
+ the DHCP options to be set.
"""
dhcp_opts = []
+ ip_version = int(CONF.pxe.ip_version)
+ if ip_version == 4:
+ boot_file_param = DHCP_BOOTFILE_NAME
+ else:
+ # NOTE(TheJulia): Booting with v6 means it is always
+ # a URL reply.
+ boot_file_param = DHCPV6_BOOTFILE_NAME
+ url_boot = True
+ # NOTE(TheJulia): The ip_version value config from the PXE config is
+ # guarded in the configuration, so there is no real sense in having
+ # anything else here in the event the value is something aside from
+ # 4 or 6, as there are no other possible values.
+ boot_file = _dhcp_option_file_or_url(task, url_boot)
- boot_file = deploy_utils.get_pxe_boot_file(task.node)
-
- if is_ipxe_enabled(task):
+ if ipxe_enabled:
script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
ipxe_script_url = '/'.join([CONF.deploy.http_url, script_name])
dhcp_provider_name = CONF.dhcp.dhcp_provider
@@ -409,7 +457,7 @@ def dhcp_options_for_instance(task):
if dhcp_provider_name == 'neutron':
# Neutron use dnsmasq as default DHCP agent, add extra config
# to neutron "dhcp-match=set:ipxe,175" and use below option
- dhcp_opts.append({'opt_name': "tag:!ipxe,%s" % DHCP_BOOTFILE_NAME,
+ dhcp_opts.append({'opt_name': "tag:!ipxe,%s" % boot_file_param,
'opt_value': boot_file})
dhcp_opts.append({'opt_name': "tag:ipxe,%s" % DHCP_BOOTFILE_NAME,
'opt_value': ipxe_script_url})
@@ -417,25 +465,27 @@ def dhcp_options_for_instance(task):
# !175 == non-iPXE.
# http://ipxe.org/howto/dhcpd#ipxe-specific_options
dhcp_opts.append({'opt_name': "!%s,%s" % (DHCP_IPXE_ENCAP_OPTS,
- DHCP_BOOTFILE_NAME),
+ boot_file_param),
'opt_value': boot_file})
- dhcp_opts.append({'opt_name': DHCP_BOOTFILE_NAME,
+ dhcp_opts.append({'opt_name': boot_file_param,
'opt_value': ipxe_script_url})
else:
- dhcp_opts.append({'opt_name': DHCP_BOOTFILE_NAME,
+ dhcp_opts.append({'opt_name': boot_file_param,
'opt_value': boot_file})
# 210 == tftp server path-prefix or tftp root, will be used to find
# pxelinux.cfg directory. The pxelinux.0 loader infers this information
# from it's own path, but Petitboot needs it to be specified by this
# option since it doesn't use pxelinux.0 loader.
- dhcp_opts.append({'opt_name': DHCP_TFTP_PATH_PREFIX,
- 'opt_value': get_tftp_path_prefix()})
-
- dhcp_opts.append({'opt_name': DHCP_TFTP_SERVER_NAME,
- 'opt_value': CONF.pxe.tftp_server})
- dhcp_opts.append({'opt_name': DHCP_TFTP_SERVER_ADDRESS,
- 'opt_value': CONF.pxe.tftp_server})
-
+ if not url_boot:
+ dhcp_opts.append(
+ {'opt_name': DHCP_TFTP_PATH_PREFIX,
+ 'opt_value': get_tftp_path_prefix()})
+
+ if not url_boot:
+ dhcp_opts.append({'opt_name': DHCP_TFTP_SERVER_NAME,
+ 'opt_value': CONF.pxe.tftp_server})
+ dhcp_opts.append({'opt_name': DHCP_TFTP_SERVER_ADDRESS,
+ 'opt_value': CONF.pxe.tftp_server})
# NOTE(vsaienko) set this option specially for dnsmasq case as it always
# sets `siaddr` field which is treated by pxe clients as TFTP server
# see page 9 https://tools.ietf.org/html/rfc2131.
@@ -449,12 +499,13 @@ def dhcp_options_for_instance(task):
# unknown options but potentially it may blow up with others.
# Related bug was opened on Neutron side:
# https://bugs.launchpad.net/neutron/+bug/1723354
- dhcp_opts.append({'opt_name': 'server-ip-address',
- 'opt_value': CONF.pxe.tftp_server})
+ if not url_boot:
+ dhcp_opts.append({'opt_name': 'server-ip-address',
+ 'opt_value': CONF.pxe.tftp_server})
# Append the IP version for all the configuration options
for opt in dhcp_opts:
- opt.update({'ip_version': int(CONF.pxe.ip_version)})
+ opt.update({'ip_version': ip_version})
return dhcp_opts
@@ -593,10 +644,10 @@ def get_image_info(node, mode='deploy'):
def build_deploy_pxe_options(task, pxe_info, mode='deploy'):
pxe_opts = {}
node = task.node
-
+ # TODO(TheJulia): In the future this should become an argument
+ ipxe_enabled = is_ipxe_enabled(task)
kernel_label = '%s_kernel' % mode
ramdisk_label = '%s_ramdisk' % mode
- ipxe_enabled = is_ipxe_enabled(task)
for label, option in ((kernel_label, 'deployment_aki_path'),
(ramdisk_label, 'deployment_ari_path')):
if ipxe_enabled:
@@ -839,7 +890,7 @@ def prepare_instance_pxe_config(task, image_info,
"""
node = task.node
- dhcp_opts = dhcp_options_for_instance(task)
+ dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled)
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
pxe_config_path = get_pxe_config_file_path(
diff --git a/ironic/drivers/modules/ipxe.py b/ironic/drivers/modules/ipxe.py
index e1f6069e8..4eb687eb8 100644
--- a/ironic/drivers/modules/ipxe.py
+++ b/ironic/drivers/modules/ipxe.py
@@ -142,7 +142,8 @@ class iPXEBoot(pxe_base.PXEBaseMixin, base.BootInterface):
# or was deleted.
pxe_utils.create_ipxe_boot_script()
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
@@ -219,7 +220,8 @@ class iPXEBoot(pxe_base.PXEBaseMixin, base.BootInterface):
pxe_utils.cache_ramdisk_kernel(task, instance_image_info)
# If it's going to PXE boot we need to update the DHCP server
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task,
+ ipxe_enabled=True)
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
diff --git a/ironic/drivers/modules/pxe.py b/ironic/drivers/modules/pxe.py
index 017a63199..393dde424 100644
--- a/ironic/drivers/modules/pxe.py
+++ b/ironic/drivers/modules/pxe.py
@@ -141,8 +141,8 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface):
"""
node = task.node
mode = deploy_utils.rescue_or_deploy_mode(node)
-
- if CONF.pxe.ipxe_enabled:
+ ipxe_enabled = CONF.pxe.ipxe_enabled
+ if ipxe_enabled:
# NOTE(mjturek): At this point, the ipxe boot script should
# already exist as it is created at startup time. However, we
# call the boot script create method here to assert its
@@ -150,7 +150,8 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface):
# or was deleted.
pxe_utils.create_ipxe_boot_script()
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=ipxe_enabled)
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
@@ -224,7 +225,8 @@ class PXEBoot(pxe_base.PXEBaseMixin, base.BootInterface):
pxe_utils.cache_ramdisk_kernel(task, instance_image_info)
# If it's going to PXE boot we need to update the DHCP server
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled)
provider = dhcp_factory.DHCPFactory()
provider.update_dhcp(task, dhcp_opts)
diff --git a/ironic/tests/unit/common/test_pxe_utils.py b/ironic/tests/unit/common/test_pxe_utils.py
index 498d00615..2f5884d36 100644
--- a/ironic/tests/unit/common/test_pxe_utils.py
+++ b/ironic/tests/unit/common/test_pxe_utils.py
@@ -719,22 +719,33 @@ class TestPXEUtils(db_base.DbTestCase):
self.config(tftp_server='192.0.2.1', group='pxe')
self.config(pxe_bootfile_name='fake-bootfile', group='pxe')
self.config(tftp_root='/tftp-path/', group='pxe')
- expected_info = [{'opt_name': '67',
- 'opt_value': 'fake-bootfile',
- 'ip_version': ip_version},
- {'opt_name': '210',
- 'opt_value': '/tftp-path/',
- 'ip_version': ip_version},
- {'opt_name': '66',
- 'opt_value': '192.0.2.1',
- 'ip_version': ip_version},
- {'opt_name': '150',
- 'opt_value': '192.0.2.1',
- 'ip_version': ip_version},
- {'opt_name': 'server-ip-address',
- 'opt_value': '192.0.2.1',
- 'ip_version': ip_version}
- ]
+
+ if ip_version == 6:
+ # NOTE(TheJulia): DHCPv6 RFCs seem to indicate that the prior
+ # options are not imported, although they may be supported
+ # by vendors. The apparent proper option is to return a
+ # URL in the field https://tools.ietf.org/html/rfc5970#section-3
+ expected_info = [{'opt_name': '59',
+ 'opt_value': 'tftp://192.0.2.1/tftp-path'
+ '/fake-bootfile',
+ 'ip_version': ip_version}]
+ elif ip_version == 4:
+ expected_info = [{'opt_name': '67',
+ 'opt_value': 'fake-bootfile',
+ 'ip_version': ip_version},
+ {'opt_name': '210',
+ 'opt_value': '/tftp-path/',
+ 'ip_version': ip_version},
+ {'opt_name': '66',
+ 'opt_value': '192.0.2.1',
+ 'ip_version': ip_version},
+ {'opt_name': '150',
+ 'opt_value': '192.0.2.1',
+ 'ip_version': ip_version},
+ {'opt_name': 'server-ip-address',
+ 'opt_value': '192.0.2.1',
+ 'ip_version': ip_version}
+ ]
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertEqual(expected_info,
pxe_utils.dhcp_options_for_instance(task))
@@ -817,7 +828,8 @@ class TestPXEUtils(db_base.DbTestCase):
'ip_version': 4}]
self.assertItemsEqual(expected_info,
- pxe_utils.dhcp_options_for_instance(task))
+ pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True))
self.config(dhcp_provider='neutron', group='dhcp')
expected_boot_script_url = 'http://192.0.3.2:1234/boot.ipxe'
@@ -838,7 +850,8 @@ class TestPXEUtils(db_base.DbTestCase):
'ip_version': 4}]
self.assertItemsEqual(expected_info,
- pxe_utils.dhcp_options_for_instance(task))
+ pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True))
def test_dhcp_options_for_instance_ipxe_bios(self):
boot_file = 'fake-bootfile-bios'
diff --git a/ironic/tests/unit/drivers/modules/test_ipxe.py b/ironic/tests/unit/drivers/modules/test_ipxe.py
index 974a5ff5f..e45f9d65f 100644
--- a/ironic/tests/unit/drivers/modules/test_ipxe.py
+++ b/ironic/tests/unit/drivers/modules/test_ipxe.py
@@ -257,7 +257,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
'rescue_ramdisk': 'r'}
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'})
mock_deploy_img_info.assert_called_once_with(task.node, mode=mode)
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
@@ -538,7 +539,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
'ramdisk': ('', '/path/to/ramdisk')}
get_image_info_mock.return_value = image_info
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
@@ -578,7 +580,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
self.node.provision_state = states.ACTIVE
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
@@ -615,7 +618,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
'ramdisk': ('', '/path/to/ramdisk')}
get_image_info_mock.return_value = image_info
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
task.node.properties['capabilities'] = 'boot_mode:bios'
task.node.driver_internal_info['is_whole_disk_image'] = False
@@ -644,7 +648,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
dhcp_factory_mock.return_value = provider_mock
get_image_info_mock.return_value = {}
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=True)
task.node.properties['capabilities'] = 'boot_mode:bios'
task.node.driver_internal_info['is_whole_disk_image'] = True
task.driver.boot.prepare_instance(task)
@@ -688,7 +693,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_internal_info = {
'boot_from_volume': vol_id}
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task,
+ ipxe_enabled=True)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
diff --git a/ironic/tests/unit/drivers/modules/test_pxe.py b/ironic/tests/unit/drivers/modules/test_pxe.py
index 63e606728..8e7887784 100644
--- a/ironic/tests/unit/drivers/modules/test_pxe.py
+++ b/ironic/tests/unit/drivers/modules/test_pxe.py
@@ -255,7 +255,8 @@ class PXEBootTestCase(db_base.DbTestCase):
'rescue_ramdisk': 'r'}
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'})
mock_deploy_img_info.assert_called_once_with(task.node, mode=mode)
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
@@ -543,7 +544,8 @@ class PXEBootTestCase(db_base.DbTestCase):
'ramdisk': ('', '/path/to/ramdisk')}
get_image_info_mock.return_value = image_info
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
@@ -583,7 +585,8 @@ class PXEBootTestCase(db_base.DbTestCase):
self.node.provision_state = states.ACTIVE
self.node.save()
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
@@ -621,7 +624,8 @@ class PXEBootTestCase(db_base.DbTestCase):
'ramdisk': ('', '/path/to/ramdisk')}
get_image_info_mock.return_value = image_info
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
task.node.properties['capabilities'] = 'boot_mode:bios'
task.node.driver_internal_info['is_whole_disk_image'] = False
@@ -647,7 +651,8 @@ class PXEBootTestCase(db_base.DbTestCase):
dhcp_factory_mock.return_value = provider_mock
get_image_info_mock.return_value = {}
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, CONF.pxe.ipxe_enabled)
task.node.properties['capabilities'] = 'boot_mode:bios'
task.node.driver_internal_info['is_whole_disk_image'] = True
task.driver.boot.prepare_instance(task)
@@ -691,7 +696,8 @@ class PXEBootTestCase(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_internal_info = {
'boot_from_volume': vol_id}
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(task,
+ ipxe_enabled=True)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_mode:bios'
@@ -780,7 +786,8 @@ class PXEBootTestCase(db_base.DbTestCase):
instance_info['capabilities'] = {'boot_option': 'ramdisk'}
task.node.instance_info = instance_info
task.node.save()
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.driver.boot.prepare_instance(task)
@@ -871,7 +878,8 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase):
'ramdisk': ('', '/path/to/ramdisk')}
get_image_info_mock.return_value = image_info
with task_manager.acquire(self.context, self.node.uuid) as task:
- dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
+ dhcp_opts = pxe_utils.dhcp_options_for_instance(
+ task, ipxe_enabled=CONF.pxe.ipxe_enabled)
pxe_config_path = pxe_utils.get_pxe_config_file_path(
task.node.uuid)
task.node.properties['capabilities'] = 'boot_option:netboot'
diff --git a/releasenotes/notes/boot-from-url-98d21670e726c518.yaml b/releasenotes/notes/boot-from-url-98d21670e726c518.yaml
new file mode 100644
index 000000000..c97c59575
--- /dev/null
+++ b/releasenotes/notes/boot-from-url-98d21670e726c518.yaml
@@ -0,0 +1,9 @@
+---
+fixes:
+ - |
+ Fixes a misunderstanding in how DHCPv6 booting of machines operates
+ in that only a URL to the boot loader is expected in that case, as opposed
+ to traditional TFTP parameters. Now a URL is sent to the client in the form
+ of ``tftp://<tftp_address>/<tftp_path>/<boot_file>``. See
+ `story 1744620 <https://storyboard.openstack.org/#!/story/1744620>`_
+ for more information.