Skip to content

Commit 15dd17d

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Allow to pass EUI64 IP address as fixed ip for the port" into stable/yoga
2 parents ad778a6 + bf1e9e4 commit 15dd17d

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)