Additional IP addresses to IPv6 stateful ports
When network booting with DHCPv6-stateful we cannot control the CLID/IAID used by the different clients, UEFI, iPXE, Ironic IPA etc. Multiple IP address reservation is required in the DHCPv6 server to avoid NoAddrsAvail issues. A new option to control the number of addresses to allocate for IPv6 stateful ports created by Ironic for provisioning, cleaning, rescuing etc is added. Story: 2007315 Task: 38820 Change-Id: I8c595830b8c7974c651a9e524b57a6540e4f3d1f
This commit is contained in:
parent
c40d221fca
commit
cb3185274a
@ -12,6 +12,7 @@
|
||||
|
||||
import copy
|
||||
|
||||
import netaddr
|
||||
from neutronclient.common import exceptions as neutron_exceptions
|
||||
from neutronclient.v2_0 import client as clientv20
|
||||
from oslo_log import log
|
||||
@ -192,6 +193,32 @@ def _verify_security_groups(security_groups, client):
|
||||
raise exception.NetworkError(msg)
|
||||
|
||||
|
||||
def _add_ip_addresses_for_ipv6_stateful(port, client):
|
||||
"""Add additional IP addresses to the ipv6 stateful neutron port
|
||||
|
||||
When network booting with DHCPv6-stateful we cannot control the CLID/IAID
|
||||
used by the different clients, UEFI, iPXE, Ironic IPA etc. Multiple
|
||||
IP address reservation is required in the DHCPv6 server to avoid
|
||||
NoAddrsAvail issues.
|
||||
|
||||
:param port: A neutron port
|
||||
:param client: Neutron client
|
||||
"""
|
||||
fixed_ips = port['port']['fixed_ips']
|
||||
if (not fixed_ips
|
||||
or netaddr.IPAddress(fixed_ips[0]['ip_address']).version != 6):
|
||||
return
|
||||
|
||||
subnet = client.show_subnet(
|
||||
port['port']['fixed_ips'][0]['subnet_id']).get('subnet')
|
||||
if subnet and subnet['ipv6_address_mode'] == 'dhcpv6-stateful':
|
||||
for i in range(1, CONF.neutron.dhcpv6_stateful_address_count):
|
||||
fixed_ips.append({'subnet_id': subnet['id']})
|
||||
|
||||
body = {'port': {'fixed_ips': fixed_ips}}
|
||||
client.update_port(port['port']['id'], body)
|
||||
|
||||
|
||||
def add_ports_to_network(task, network_uuid, security_groups=None):
|
||||
"""Create neutron ports to boot the ramdisk.
|
||||
|
||||
@ -295,6 +322,8 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
|
||||
wait_for_host_agent(client,
|
||||
port_body['port']['binding:host_id'])
|
||||
port = client.create_port(port_body)
|
||||
if CONF.neutron.dhcpv6_stateful_address_count > 1:
|
||||
_add_ip_addresses_for_ipv6_stateful(port, client)
|
||||
if is_smart_nic:
|
||||
wait_for_port_status(client, port['port']['id'], 'ACTIVE')
|
||||
except neutron_exceptions.NeutronClientException as e:
|
||||
|
@ -102,6 +102,16 @@ opts = [
|
||||
'"neutron" network interface and not used for the '
|
||||
'"flat" or "noop" network interfaces. If not '
|
||||
'specified, the default security group is used.')),
|
||||
cfg.IntOpt('dhcpv6_stateful_address_count',
|
||||
default=4,
|
||||
help=_('Number of IPv6 addresses to allocate for ports created '
|
||||
'for provisioning, cleaning, rescue or inspection on '
|
||||
'DHCPv6-stateful networks. Different stages of the '
|
||||
'chain-loading process will request addresses with '
|
||||
'different CLID/IAID. Due to non-identical identifiers '
|
||||
'multiple addresses must be reserved for the host to '
|
||||
'ensure each step of the boot process can successfully '
|
||||
'lease addresses.'))
|
||||
]
|
||||
|
||||
|
||||
|
@ -161,7 +161,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
)]
|
||||
# Very simple neutron port representation
|
||||
self.neutron_port = {'id': '132f871f-eaec-4fed-9475-0d54465e0f00',
|
||||
'mac_address': '52:54:00:cf:2d:32'}
|
||||
'mac_address': '52:54:00:cf:2d:32',
|
||||
'fixed_ips': []}
|
||||
self.network_uuid = uuidutils.generate_uuid()
|
||||
self.client_mock = mock.Mock()
|
||||
self.client_mock.list_agents.return_value = {
|
||||
@ -217,7 +218,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
expected_body2['port']['mac_address'] = port2.address
|
||||
expected_body2['fixed_ips'] = []
|
||||
neutron_port2 = {'id': '132f871f-eaec-4fed-9475-0d54465e0f01',
|
||||
'mac_address': port2.address}
|
||||
'mac_address': port2.address,
|
||||
'fixed_ips': []}
|
||||
self.client_mock.create_port.side_effect = [
|
||||
{'port': self.neutron_port},
|
||||
{'port': neutron_port2}
|
||||
@ -259,6 +261,36 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
self._test_add_ports_to_network(is_client_id=False,
|
||||
security_groups=sg_ids)
|
||||
|
||||
def test__add_ip_addresses_for_ipv6_stateful(self):
|
||||
subnet_id = uuidutils.generate_uuid()
|
||||
self.client_mock.show_subnet.return_value = {
|
||||
'subnet': {
|
||||
'id': subnet_id,
|
||||
'ip_version': 6,
|
||||
'ipv6_address_mode': 'dhcpv6-stateful'
|
||||
}
|
||||
}
|
||||
self.neutron_port['fixed_ips'] = [{'subnet_id': subnet_id,
|
||||
'ip_address': '2001:db8::1'}]
|
||||
|
||||
expected_body = {
|
||||
'port': {
|
||||
'fixed_ips': [
|
||||
{'subnet_id': subnet_id, 'ip_address': '2001:db8::1'},
|
||||
{'subnet_id': subnet_id},
|
||||
{'subnet_id': subnet_id},
|
||||
{'subnet_id': subnet_id}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
neutron._add_ip_addresses_for_ipv6_stateful(
|
||||
{'port': self.neutron_port},
|
||||
self.client_mock
|
||||
)
|
||||
self.client_mock.update_port.assert_called_once_with(
|
||||
self.neutron_port['id'], expected_body)
|
||||
|
||||
def test_verify_sec_groups(self):
|
||||
sg_ids = []
|
||||
for i in range(2):
|
||||
@ -655,6 +687,19 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
|
||||
self.assertFalse(res)
|
||||
self.assertTrue(log_mock.error.called)
|
||||
|
||||
@mock.patch.object(neutron, 'LOG', autospec=True)
|
||||
def test_validate_port_info_neutron_with_network_type_unmanaged(
|
||||
self, log_mock):
|
||||
self.node.network_interface = 'neutron'
|
||||
self.node.save()
|
||||
llc = {'network_type': 'unmanaged'}
|
||||
port = object_utils.create_test_port(
|
||||
self.context, node_id=self.node.id, uuid=uuidutils.generate_uuid(),
|
||||
address='52:54:00:cf:2d:33', local_link_connection=llc)
|
||||
res = neutron.validate_port_info(self.node, port)
|
||||
self.assertTrue(res)
|
||||
self.assertFalse(log_mock.warning.called)
|
||||
|
||||
def test_validate_agent_up(self):
|
||||
self.client_mock.list_agents.return_value = {
|
||||
'agents': [{'alive': True}]}
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
For baremetal operations on DHCPv6-stateful networks multiple IPv6
|
||||
addresses can now be allocated for neutron ports created for provisioning,
|
||||
cleaning, rescue or inspection. The new parameter
|
||||
``[neutron]/dhcpv6_stateful_address_count`` controls the number of addresses
|
||||
to allocate (Default: 4).
|
||||
|
Loading…
x
Reference in New Issue
Block a user