summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDane LeBlanc <leblancd@cisco.com>2014-11-17 10:40:04 -0500
committerafazekas <afazekas@redhat.com>2014-11-25 12:01:08 +0000
commit63b43376b3818a28af496d3a6f4e6957df9427f2 (patch)
tree4f96d4a153159513e007afd6513436dc6f73a543
parent581977d958a456294044043e0c422303ba5a1def (diff)
downloadneutron-63b43376b3818a28af496d3a6f4e6957df9427f2.tar.gz
Subnet delete for IPv6 SLAAC should not require prior port disassoc
With the current Neutron implementation, a subnet cannot be deleted until all associated IP addresses have been remove from ports (via port update) or the associated ports/VMs have been deleted. In the case of SLAAC-enabled subnets, however, it's not feasible to require removal of SLAAC-generated addresses individually from each associated port before deleting a subnet because of the multicast nature of RA messages. For SLAAC-enabled subnets, the processing of subnet delete requests needs to be changed so that these subnets will be allowed to be deleted, and all ports get disassociated from their corresponding SLAAC IP address, when there are ports existing on the SLAAC subnet. Change-Id: I281f5a1553248e09174dc49d0a42aef4b5c44bee Closes-Bug: 1393435 (cherry picked from commit ce5e95723925b0d7750bb3daa5be54765985cc30)
-rw-r--r--neutron/db/db_base_plugin_v2.py8
-rw-r--r--neutron/plugins/ml2/plugin.py4
-rw-r--r--neutron/tests/unit/test_db_plugin.py30
3 files changed, 39 insertions, 3 deletions
diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py
index 70827c58af..903332de0a 100644
--- a/neutron/db/db_base_plugin_v2.py
+++ b/neutron/db/db_base_plugin_v2.py
@@ -1276,9 +1276,13 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
filter_by(network_id=subnet['network_id']).
with_lockmode('update'))
- # remove network owned ports
+ # Remove network owned ports, and delete IP allocations
+ # for IPv6 addresses which were automatically generated
+ # via SLAAC
+ is_ipv6_slaac_subnet = ipv6_utils.is_slaac_subnet(subnet)
for a in allocated:
- if a.ports.device_owner in AUTO_DELETE_PORT_OWNERS:
+ if (is_ipv6_slaac_subnet or
+ a.ports.device_owner in AUTO_DELETE_PORT_OWNERS):
NeutronDbPluginV2._delete_ip_allocation(
context, subnet.network_id, id, a.ip_address)
else:
diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py
index d29deda6ce..fe3a6face8 100644
--- a/neutron/plugins/ml2/plugin.py
+++ b/neutron/plugins/ml2/plugin.py
@@ -29,6 +29,7 @@ from neutron.api.rpc.handlers import securitygroups_rpc
from neutron.api.v2 import attributes
from neutron.common import constants as const
from neutron.common import exceptions as exc
+from neutron.common import ipv6_utils
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron.common import utils
@@ -726,7 +727,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
LOG.debug(_("Ports to auto-deallocate: %s"), allocated)
only_auto_del = all(not a.port_id or
a.ports.device_owner in db_base_plugin_v2.
- AUTO_DELETE_PORT_OWNERS
+ AUTO_DELETE_PORT_OWNERS or
+ ipv6_utils.is_slaac_subnet(subnet)
for a in allocated)
if not only_auto_del:
LOG.debug(_("Tenant-owned ports exist"))
diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py
index 24eab08440..ec624c59fb 100644
--- a/neutron/tests/unit/test_db_plugin.py
+++ b/neutron/tests/unit/test_db_plugin.py
@@ -2641,6 +2641,36 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(res.status_int,
webob.exc.HTTPNoContent.code)
+ def test_delete_subnet_ipv6_slaac_port_exists(self):
+ """Test IPv6 SLAAC subnet delete when a port is still using subnet."""
+ res = self._create_network(fmt=self.fmt, name='net',
+ admin_state_up=True)
+ network = self.deserialize(self.fmt, res)
+ # Create an IPv6 SLAAC subnet and a port using that subnet
+ subnet = self._make_subnet(self.fmt, network, gateway='fe80::1',
+ cidr='fe80::/64', ip_version=6,
+ ipv6_ra_mode=constants.IPV6_SLAAC,
+ ipv6_address_mode=constants.IPV6_SLAAC)
+ res = self._create_port(self.fmt, net_id=network['network']['id'])
+ port = self.deserialize(self.fmt, res)
+ self.assertEqual(1, len(port['port']['fixed_ips']))
+
+ # The port should have an address from the subnet
+ req = self.new_show_request('ports', port['port']['id'], self.fmt)
+ res = req.get_response(self.api)
+ sport = self.deserialize(self.fmt, req.get_response(self.api))
+ self.assertEqual(1, len(sport['port']['fixed_ips']))
+
+ # Delete the subnet
+ req = self.new_delete_request('subnets', subnet['subnet']['id'])
+ res = req.get_response(self.api)
+ self.assertEqual(webob.exc.HTTPNoContent.code, res.status_int)
+ # The port should no longer have an address from the deleted subnet
+ req = self.new_show_request('ports', port['port']['id'], self.fmt)
+ res = req.get_response(self.api)
+ sport = self.deserialize(self.fmt, req.get_response(self.api))
+ self.assertEqual(0, len(sport['port']['fixed_ips']))
+
def test_delete_network(self):
gateway_ip = '10.0.0.1'
cidr = '10.0.0.0/24'