Skip to content

Commit bf1e9e4

Browse files
committed
Allow to pass EUI64 IP address as fixed ip for the port
When port with IP address from the auto allocation subnet (SLAAC or dhcp_stateless) is created or updated and fixed IP address is specified, Neutron will fail as in case of such subnets IP address is assigned automatically based on the subnet's prefix and port's MAC address. But in case when given IP address is actually correct EUI64 generated IP address (the same as Neutron would generate for that port) there is no need to raise an exception and fail request. Additionally this patch fixes imports sections in the ipam_pluggable_backend module. Closes-bug: #1991398 Change-Id: Iaee5608c9581228a83f7ad75dbf2cc31dafaa9ea (cherry picked from commit d7b44f7)
1 parent 4c842e8 commit bf1e9e4

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

neutron/db/ipam_pluggable_backend.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
from neutron_lib.objects import utils as obj_utils
2424
from neutron_lib.plugins import constants as plugin_consts
2525
from neutron_lib.plugins import directory
26-
2726
from oslo_db import exception as db_exc
2827
from oslo_log import log as logging
2928
from oslo_utils import excutils
29+
from oslo_utils import netutils
3030

3131
from neutron.common import ipv6_utils
3232
from neutron.db import ipam_backend_mixin
@@ -272,6 +272,7 @@ def _allocate_ips_for_port(self, context, port):
272272
p["network_id"],
273273
p['fixed_ips'],
274274
p['device_owner'],
275+
p['mac_address'],
275276
subnets)
276277
else:
277278
ips = []
@@ -304,7 +305,7 @@ def _get_auto_address_ips(self, v6_stateless_subnets, port,
304305
return ips
305306

306307
def _test_fixed_ips_for_port(self, context, network_id, fixed_ips,
307-
device_owner, subnets):
308+
device_owner, mac_address, subnets):
308309
"""Test fixed IPs for port.
309310
310311
Check that configured subnets are valid prior to allocating any
@@ -325,8 +326,11 @@ def _test_fixed_ips_for_port(self, context, network_id, fixed_ips,
325326
subnet['cidr'] != constants.PROVISIONAL_IPV6_PD_PREFIX):
326327
if (is_auto_addr_subnet and device_owner not in
327328
constants.ROUTER_INTERFACE_OWNERS):
328-
raise ipam_exc.AllocationOnAutoAddressSubnet(
329-
ip=fixed['ip_address'], subnet_id=subnet['id'])
329+
eui64_ip = netutils.get_ipv6_addr_by_EUI64(
330+
subnet['cidr'], mac_address)
331+
if eui64_ip != netaddr.IPAddress(fixed['ip_address']):
332+
raise ipam_exc.AllocationOnAutoAddressSubnet(
333+
ip=fixed['ip_address'], subnet_id=subnet['id'])
330334
fixed_ip_list.append({'subnet_id': subnet['id'],
331335
'ip_address': fixed['ip_address']})
332336
else:
@@ -383,7 +387,7 @@ def _update_ips_for_port(self, context, port, host,
383387
# Check if the IP's to add are OK
384388
to_add = self._test_fixed_ips_for_port(
385389
context, port['network_id'], changes.add,
386-
port['device_owner'], subnets)
390+
port['device_owner'], port['mac_address'], subnets)
387391

388392
if port['device_owner'] not in constants.ROUTER_INTERFACE_OWNERS:
389393
to_add += self._update_ips_for_pd_subnet(

neutron/tests/unit/db/test_ipam_pluggable_backend.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
from neutron.db import ipam_backend_mixin
3030
from neutron.db import ipam_pluggable_backend
31+
from neutron.ipam import exceptions as ipam_exc
3132
from neutron.ipam import requests as ipam_req
3233
from neutron.objects import network as network_obj
3334
from neutron.objects import ports as port_obj
@@ -382,6 +383,7 @@ def test_test_fixed_ips_for_port_pd_gateway(self):
382383
subnet['network_id'],
383384
fixed_ips,
384385
constants.DEVICE_OWNER_ROUTER_INTF,
386+
"aa:bb:cc:dd:ee:ff",
385387
[subnet]))
386388
# Assert that ports created on prefix delegation subnets
387389
# will be returned without an ip address. This prevents router
@@ -390,6 +392,31 @@ def test_test_fixed_ips_for_port_pd_gateway(self):
390392
self.assertEqual(subnet['id'], filtered_ips[0]['subnet_id'])
391393
self.assertNotIn('ip_address', filtered_ips[0])
392394

395+
def test_test_fixed_ips_for_port_allocation_on_auto_address_subnet(self):
396+
context = mock.Mock()
397+
pluggable_backend = ipam_pluggable_backend.IpamPluggableBackend()
398+
with self.subnet(cidr="2001:db8::/64",
399+
ip_version=constants.IP_VERSION_6,
400+
ipv6_ra_mode=constants.IPV6_SLAAC,
401+
ipv6_address_mode=constants.IPV6_SLAAC) as subnet:
402+
subnet = subnet['subnet']
403+
bad_fixed_ip = [{'subnet_id': subnet['id'],
404+
'ip_address': '2001:db8::22'}]
405+
eui64_fixed_ip = [{'subnet_id': subnet['id'],
406+
'ip_address': '2001:db8::a8bb:ccff:fedd:eeff'}]
407+
self.assertRaises(
408+
ipam_exc.AllocationOnAutoAddressSubnet,
409+
pluggable_backend._test_fixed_ips_for_port,
410+
context, subnet['network_id'], bad_fixed_ip,
411+
"device_owner", "aa:bb:cc:dd:ee:ff",
412+
[subnet])
413+
414+
filtered_ips = pluggable_backend._test_fixed_ips_for_port(
415+
context, subnet['network_id'], eui64_fixed_ip, "device_owner",
416+
"aa:bb:cc:dd:ee:ff", [subnet])
417+
self.assertEqual(1, len(filtered_ips))
418+
self.assertEqual(subnet['id'], filtered_ips[0]['subnet_id'])
419+
393420
@mock.patch('neutron.ipam.driver.Pool')
394421
def test_create_subnet_over_ipam(self, pool_mock):
395422
mocks = self._prepare_mocks_with_pool_mock(pool_mock)
@@ -686,6 +713,7 @@ def test_update_ips_for_port_passes_port_dict_to_factory(self, pool_mock):
686713
mocks['ipam']._update_ips_for_pd_subnet = mock.Mock(return_value=[])
687714

688715
port_dict = {'device_owner': uuidutils.generate_uuid(),
716+
'mac_address': 'aa:bb:cc:dd:ee:ff',
689717
'network_id': uuidutils.generate_uuid()}
690718

691719
mocks['ipam']._update_ips_for_port(context, port_dict, None,
@@ -726,6 +754,7 @@ def test_update_ips_for_port_ovn_distributed_svc(self, pool_mock):
726754
port_dict = {
727755
'device_owner': constants.DEVICE_OWNER_DISTRIBUTED,
728756
'device_id': 'ovnmeta-%s' % uuidutils.generate_uuid(),
757+
'mac_address': 'aa:bb:cc:dd:ee:ff',
729758
'network_id': uuidutils.generate_uuid()}
730759

731760
mocks['ipam']._update_ips_for_port(context, port_dict, None,
@@ -749,6 +778,7 @@ def test_update_ips_for_port_passes_port_id_to_factory(self, pool_mock):
749778
'subnet_id': uuidutils.generate_uuid()}
750779
port_dict = {'port': {'device_owner': uuidutils.generate_uuid(),
751780
'network_id': network_id,
781+
'mac_address': 'aa:bb:cc:dd:ee:ff',
752782
'fixed_ips': [ip_dict]}}
753783
subnets = [{'id': ip_dict['subnet_id'],
754784
'network_id': network_id,

0 commit comments

Comments
 (0)