"dual stack" support for PXE/iPXE
Adds functionality for dual stack capabilities and automatic population to neutron with the correct response based upon the IP version of the provisioning/cleaning/rescue or tenant ports. This was origianlly intended to be separated from removing the need for [pxe]ip_version, however the resulting code changes from doing both this and making ironic support dual stacks touched the same tests and some of the same code, so combined is simpler. Change-Id: If7a296001e204ae0c9a49495731052ab33379628
This commit is contained in:
parent
1dee25f554
commit
cf412bc81e
@ -373,13 +373,16 @@ def clean_up_pxe_config(task, ipxe_enabled=False):
|
||||
task.node.uuid))
|
||||
|
||||
|
||||
def _dhcp_option_file_or_url(task, urlboot=False):
|
||||
def _dhcp_option_file_or_url(task, urlboot=False, ip_version=None):
|
||||
"""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.
|
||||
:param ip_version: Integer representing the version of IP of
|
||||
to return options for DHCP. Possible options
|
||||
are 4, and 6.
|
||||
"""
|
||||
boot_file = deploy_utils.get_pxe_boot_file(task.node)
|
||||
# NOTE(TheJulia): There are additional cases as we add new
|
||||
@ -387,12 +390,16 @@ def _dhcp_option_file_or_url(task, urlboot=False):
|
||||
if not urlboot:
|
||||
return boot_file
|
||||
elif urlboot:
|
||||
host = utils.wrap_ipv6(CONF.pxe.tftp_server)
|
||||
if CONF.my_ipv6 and ip_version == 6:
|
||||
host = utils.wrap_ipv6(CONF.my_ipv6)
|
||||
else:
|
||||
host = utils.wrap_ipv6(CONF.pxe.tftp_server)
|
||||
return "tftp://{host}/{boot_file}".format(host=host,
|
||||
boot_file=boot_file)
|
||||
|
||||
|
||||
def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False,
|
||||
ip_version=None):
|
||||
"""Retrieves the DHCP PXE boot options.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
@ -404,13 +411,19 @@ def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
If [pxe]ip_version is set to `6`, then this option
|
||||
has no effect as url_boot form is required by DHCPv6
|
||||
standards.
|
||||
:param ip_version: The IP version of options to return as values
|
||||
differ by IP version. Default to [pxe]ip_version.
|
||||
Possible options are integers 4 or 6.
|
||||
:returns: Dictionary to be sent to the networking service describing
|
||||
the DHCP options to be set.
|
||||
"""
|
||||
if ip_version:
|
||||
use_ip_version = ip_version
|
||||
else:
|
||||
use_ip_version = int(CONF.pxe.ip_version)
|
||||
dhcp_opts = []
|
||||
ip_version = int(CONF.pxe.ip_version)
|
||||
dhcp_provider_name = CONF.dhcp.dhcp_provider
|
||||
if ip_version == 4:
|
||||
if use_ip_version == 4:
|
||||
boot_file_param = DHCP_BOOTFILE_NAME
|
||||
else:
|
||||
# NOTE(TheJulia): Booting with v6 means it is always
|
||||
@ -421,7 +434,7 @@ def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
# 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 = _dhcp_option_file_or_url(task, url_boot, use_ip_version)
|
||||
|
||||
if ipxe_enabled:
|
||||
# TODO(TheJulia): DHCPv6 through dnsmasq + ipxe matching simply
|
||||
@ -444,7 +457,7 @@ def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
# added in the Stein cycle which identifies the iPXE User-Class
|
||||
# directly and is only sent in DHCPv6.
|
||||
|
||||
if ip_version != 6:
|
||||
if use_ip_version != 6:
|
||||
dhcp_opts.append(
|
||||
{'opt_name': "tag:!ipxe,%s" % boot_file_param,
|
||||
'opt_value': boot_file}
|
||||
@ -463,7 +476,7 @@ def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
else:
|
||||
# !175 == non-iPXE.
|
||||
# http://ipxe.org/howto/dhcpd#ipxe-specific_options
|
||||
if ip_version == 6:
|
||||
if use_ip_version == 6:
|
||||
LOG.warning('IPv6 is enabled and the DHCP driver appears set '
|
||||
'to a plugin aside from "neutron". Node %(name)s '
|
||||
'may not receive proper DHCPv6 provided '
|
||||
@ -512,7 +525,7 @@ def dhcp_options_for_instance(task, ipxe_enabled=False, url_boot=False):
|
||||
|
||||
# Append the IP version for all the configuration options
|
||||
for opt in dhcp_opts:
|
||||
opt.update({'ip_version': ip_version})
|
||||
opt.update({'ip_version': use_ip_version})
|
||||
|
||||
return dhcp_opts
|
||||
|
||||
@ -906,7 +919,16 @@ def prepare_instance_pxe_config(task, image_info,
|
||||
"""
|
||||
|
||||
node = task.node
|
||||
dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled)
|
||||
# Generate options for both IPv4 and IPv6, and they can be
|
||||
# filtered down later based upon the port options.
|
||||
# TODO(TheJulia): This should be re-tooled during the Victoria
|
||||
# development cycle so that we call a single method and return
|
||||
# combined options. The method we currently call is relied upon
|
||||
# by two eternal projects, to changing the behavior is not ideal.
|
||||
dhcp_opts = dhcp_options_for_instance(task, ipxe_enabled,
|
||||
ip_version=4)
|
||||
dhcp_opts += dhcp_options_for_instance(task, ipxe_enabled,
|
||||
ip_version=6)
|
||||
provider = dhcp_factory.DHCPFactory()
|
||||
provider.update_dhcp(task, dhcp_opts)
|
||||
pxe_config_path = get_pxe_config_file_path(
|
||||
|
@ -239,9 +239,20 @@ netconf_opts = [
|
||||
cfg.StrOpt('my_ip',
|
||||
default=netutils.get_my_ipv4(),
|
||||
sample_default='127.0.0.1',
|
||||
help=_('IP address of this host. If unset, will determine the '
|
||||
'IP programmatically. If unable to do so, will use '
|
||||
'"127.0.0.1".')),
|
||||
help=_('IPv4 address of this host. If unset, will determine '
|
||||
'the IP programmatically. If unable to do so, will use '
|
||||
'"127.0.0.1". NOTE: This field does accept an IPv6 '
|
||||
'address as an override for templates and URLs, '
|
||||
'however it is recommended that [DEFAULT]my_ipv6 '
|
||||
'is used along with DNS names for service URLs for '
|
||||
'dual-stack environments.')),
|
||||
cfg.StrOpt('my_ipv6',
|
||||
default=None,
|
||||
sample_default='2001:db8::1',
|
||||
help=_('IP address of this host using IPv6. This value must '
|
||||
'be supplied via the configuration and cannot be '
|
||||
'adequately programmatically determined like the '
|
||||
'[DEFAULT]my_ip parameter for IPv4.')),
|
||||
]
|
||||
|
||||
notification_opts = [
|
||||
|
@ -39,9 +39,11 @@ class BaseDHCP(object, metaclass=abc.ABCMeta):
|
||||
::
|
||||
|
||||
[{'opt_name': '67',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': '66',
|
||||
'opt_value': '123.123.123.456'}]
|
||||
'opt_value': '123.123.123.456',
|
||||
'ip_version': 4}]
|
||||
:param token: An optional authentication token. Deprecated, use context
|
||||
:param context: request context
|
||||
:type context: ironic.common.context.RequestContext
|
||||
@ -63,9 +65,11 @@ class BaseDHCP(object, metaclass=abc.ABCMeta):
|
||||
::
|
||||
|
||||
[{'opt_name': '67',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': '66',
|
||||
'opt_value': '123.123.123.456'}]
|
||||
'opt_value': '123.123.123.456',
|
||||
'ip_version': 4}]
|
||||
|
||||
:param vifs: A dict with keys 'ports' and 'portgroups' and
|
||||
dicts as values. Each dict has key/value pairs of the form
|
||||
|
@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ipaddress
|
||||
import time
|
||||
|
||||
from neutronclient.common import exceptions as neutron_client_exc
|
||||
@ -49,9 +50,11 @@ class NeutronDHCPApi(base.BaseDHCP):
|
||||
::
|
||||
|
||||
[{'opt_name': '67',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': '66',
|
||||
'opt_value': '123.123.123.456'}]
|
||||
'opt_value': '123.123.123.456'},
|
||||
'ip_version': 4}]
|
||||
:param token: optional auth token. Deprecated, use context.
|
||||
:param context: request context
|
||||
:type context: ironic.common.context.RequestContext
|
||||
@ -59,8 +62,36 @@ class NeutronDHCPApi(base.BaseDHCP):
|
||||
"""
|
||||
super(NeutronDHCPApi, self).update_port_dhcp_opts(
|
||||
port_id, dhcp_options, token=token, context=context)
|
||||
port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}}
|
||||
try:
|
||||
neutron_client = neutron.get_client(token=token,
|
||||
context=context)
|
||||
|
||||
fip = None
|
||||
port = neutron_client.show_port(port_id).get('port')
|
||||
try:
|
||||
if port:
|
||||
# TODO(TheJulia): We need to retool this down the
|
||||
# road so that we handle ports and allow preferences
|
||||
# for multi-address ports with different IP versions
|
||||
# and enable operators to possibly select preferences
|
||||
# for provisionioning operations.
|
||||
# This is compounded by v6 mainly only being available
|
||||
# with UEFI machines, so the support matrix also gets
|
||||
# a little "weird".
|
||||
# Ideally, we should work on this in Victoria.
|
||||
fip = port.get('fixed_ips')[0]
|
||||
except (TypeError, IndexError):
|
||||
fip = None
|
||||
update_opts = []
|
||||
if fip:
|
||||
ip_version = ipaddress.ip_address(fip['ip_address']).version
|
||||
for option in dhcp_options:
|
||||
if option.get('ip_version', 4) == ip_version:
|
||||
update_opts.append(option)
|
||||
else:
|
||||
LOG.error('Requested to update port for port %s, '
|
||||
'however port lacks an IP address.', port_id)
|
||||
port_req_body = {'port': {'extra_dhcp_opts': update_opts}}
|
||||
neutron.update_neutron_port(context, port_id, port_req_body)
|
||||
except neutron_client_exc.NeutronClientException:
|
||||
LOG.exception("Failed to update Neutron port %s.", port_id)
|
||||
@ -75,9 +106,11 @@ class NeutronDHCPApi(base.BaseDHCP):
|
||||
::
|
||||
|
||||
[{'opt_name': '67',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': '66',
|
||||
'opt_value': '123.123.123.456'}]
|
||||
'opt_value': '123.123.123.456',
|
||||
'ip_version': 4}]
|
||||
:param vifs: a dict of Neutron port/portgroup dicts
|
||||
to update DHCP options on. The port/portgroup dict
|
||||
key should be Ironic port UUIDs, and the values
|
||||
|
@ -169,8 +169,16 @@ class PXEBaseMixin(object):
|
||||
# or was deleted.
|
||||
pxe_utils.create_ipxe_boot_script()
|
||||
|
||||
# Generate options for both IPv4 and IPv6, and they can be
|
||||
# filtered down later based upon the port options.
|
||||
# TODO(TheJulia): This should be re-tooled during the Victoria
|
||||
# development cycle so that we call a single method and return
|
||||
# combined options. The method we currently call is relied upon
|
||||
# by two eternal projects, to changing the behavior is not ideal.
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=self.ipxe_enabled)
|
||||
task, ipxe_enabled=self.ipxe_enabled, ip_version=4)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=self.ipxe_enabled, ip_version=6)
|
||||
provider = dhcp_factory.DHCPFactory()
|
||||
provider.update_dhcp(task, dhcp_opts)
|
||||
|
||||
@ -259,7 +267,9 @@ class PXEBaseMixin(object):
|
||||
|
||||
# If it's going to PXE boot we need to update the DHCP server
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=self.ipxe_enabled)
|
||||
task, ipxe_enabled=self.ipxe_enabled, ip_version=4)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=self.ipxe_enabled, ip_version=6)
|
||||
provider = dhcp_factory.DHCPFactory()
|
||||
provider.update_dhcp(task, dhcp_opts)
|
||||
|
||||
|
@ -48,8 +48,9 @@ class TestNeutron(db_base.DbTestCase):
|
||||
|
||||
dhcp_factory.DHCPFactory._dhcp_provider = None
|
||||
|
||||
@mock.patch('ironic.common.neutron.get_client', autospec=True)
|
||||
@mock.patch('ironic.common.neutron.update_neutron_port', autospec=True)
|
||||
def test_update_port_dhcp_opts(self, update_mock):
|
||||
def test_update_port_dhcp_opts(self, update_mock, client_mock):
|
||||
opts = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': 'pxelinux.0'},
|
||||
{'opt_name': 'tftp-server',
|
||||
@ -58,6 +59,56 @@ class TestNeutron(db_base.DbTestCase):
|
||||
'opt_value': '1.1.1.1'}]
|
||||
port_id = 'fake-port-id'
|
||||
expected = {'port': {'extra_dhcp_opts': opts}}
|
||||
port_data = {
|
||||
"id": port_id,
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "192.168.1.3",
|
||||
}
|
||||
],
|
||||
}
|
||||
client_mock.return_value.show_port.return_value = {'port': port_data}
|
||||
|
||||
api = dhcp_factory.DHCPFactory()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
api.provider.update_port_dhcp_opts(port_id, opts,
|
||||
context=task.context)
|
||||
update_mock.assert_called_once_with(
|
||||
self.context, port_id, expected)
|
||||
|
||||
@mock.patch('ironic.common.neutron.get_client', autospec=True)
|
||||
@mock.patch('ironic.common.neutron.update_neutron_port', autospec=True)
|
||||
def test_update_port_dhcp_opts_v6(self, update_mock, client_mock):
|
||||
opts = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': 'pxelinux.0',
|
||||
'ip_version': 4},
|
||||
{'opt_name': 'tftp-server',
|
||||
'opt_value': '1.1.1.1',
|
||||
'ip_version': 4},
|
||||
{'opt_name': 'server-ip-address',
|
||||
'opt_value': '1.1.1.1',
|
||||
'ip_version': 4},
|
||||
{'opt_name': 'bootfile-url',
|
||||
'opt_value': 'tftp://::1/file.name',
|
||||
'ip_version': 6}]
|
||||
port_id = 'fake-port-id'
|
||||
expected = {
|
||||
'port': {
|
||||
'extra_dhcp_opts': [{
|
||||
'opt_name': 'bootfile-url',
|
||||
'opt_value': 'tftp://::1/file.name',
|
||||
'ip_version': 6}]
|
||||
}
|
||||
}
|
||||
port_data = {
|
||||
"id": port_id,
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "2001:db8::201",
|
||||
}
|
||||
],
|
||||
}
|
||||
client_mock.return_value.show_port.return_value = {'port': port_data}
|
||||
|
||||
api = dhcp_factory.DHCPFactory()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
@ -66,10 +117,21 @@ class TestNeutron(db_base.DbTestCase):
|
||||
update_mock.assert_called_once_with(
|
||||
task.context, port_id, expected)
|
||||
|
||||
@mock.patch('ironic.common.neutron.get_client', autospec=True)
|
||||
@mock.patch('ironic.common.neutron.update_neutron_port', autospec=True)
|
||||
def test_update_port_dhcp_opts_with_exception(self, update_mock):
|
||||
def test_update_port_dhcp_opts_with_exception(self, update_mock,
|
||||
client_mock):
|
||||
opts = [{}]
|
||||
port_id = 'fake-port-id'
|
||||
port_data = {
|
||||
"id": port_id,
|
||||
"fixed_ips": [
|
||||
{
|
||||
"ip_address": "192.168.1.3",
|
||||
}
|
||||
],
|
||||
}
|
||||
client_mock.return_value.show_port.return_value = {'port': port_data}
|
||||
update_mock.side_effect = (
|
||||
neutron_client_exc.NeutronClientException())
|
||||
|
||||
|
@ -270,11 +270,14 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True)
|
||||
task, ipxe_enabled=True, ip_version=4)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'})
|
||||
mock_deploy_img_info.assert_called_once_with(task.node, mode=mode,
|
||||
ipxe_enabled=True)
|
||||
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||
provider_mock.update_dhcp.assert_called_once_with(
|
||||
task, dhcp_opts)
|
||||
if self.node.provision_state == states.DEPLOYING:
|
||||
get_boot_mode_mock.assert_called_once_with(task)
|
||||
set_boot_device_mock.assert_called_once_with(task,
|
||||
@ -630,6 +633,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid, ipxe_enabled=True)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
@ -672,6 +677,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid, ipxe_enabled=True)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
@ -710,7 +717,9 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
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, ipxe_enabled=True)
|
||||
task, ipxe_enabled=True, ip_version=4)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
|
||||
@ -742,6 +751,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.boot.prepare_instance(task)
|
||||
@ -786,6 +797,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
||||
'boot_from_volume': vol_id}
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(task,
|
||||
ipxe_enabled=True)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=True, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid, ipxe_enabled=True)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
|
@ -86,6 +86,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
self.port = obj_utils.create_test_port(self.context,
|
||||
node_id=self.node.id)
|
||||
self.config(group='conductor', api_url='http://127.0.0.1:1234/')
|
||||
self.config(my_ipv6='2001:db8::1')
|
||||
|
||||
def test_get_properties(self):
|
||||
expected = pxe_base.COMMON_PROPERTIES
|
||||
@ -267,6 +268,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
task.driver.boot.prepare_ramdisk(task, {'foo': 'bar'})
|
||||
mock_deploy_img_info.assert_called_once_with(task.node,
|
||||
mode=mode,
|
||||
@ -552,7 +555,9 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
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, ipxe_enabled=False)
|
||||
task, ipxe_enabled=False, ip_version=4)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
@ -595,6 +600,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
@ -634,6 +641,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = False
|
||||
|
||||
@ -663,6 +672,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
task.node.properties['capabilities'] = 'boot_mode:bios'
|
||||
task.node.driver_internal_info['is_whole_disk_image'] = True
|
||||
task.driver.boot.prepare_instance(task)
|
||||
@ -734,6 +745,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
||||
task.node.save()
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
task.driver.boot.prepare_instance(task)
|
||||
@ -830,6 +843,8 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False)
|
||||
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||
task, ipxe_enabled=False, ip_version=6)
|
||||
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||
task.node.uuid)
|
||||
task.node.properties['capabilities'] = 'boot_option:netboot'
|
||||
|
16
releasenotes/notes/dual-stack-ironic-493ebc7b71263aaa.yaml
Normal file
16
releasenotes/notes/dual-stack-ironic-493ebc7b71263aaa.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds functionality with neutron integration to support dual-stack
|
||||
(IPv4 and IPv6 environment configurations). This enables ironic to
|
||||
look up the attached port(s) and supply DHCP options in alignment
|
||||
with the protocol version allocated on the port.
|
||||
upgrade:
|
||||
- |
|
||||
The ``[pxe]ip_version`` setting may no longer be required depending on
|
||||
neutron integration.
|
||||
- |
|
||||
Operators that used the ``[DEFAULT]my_ip`` setting with an IPv6 address
|
||||
may wish to explore migrating to the ``[DEFAULT]my_ipv6`` setting. Setting
|
||||
both values enables the appropriate IP addresses based on protocol version
|
||||
for PXE/iPXE.
|
Loading…
Reference in New Issue
Block a user