]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Subnet delete for IPv6 SLAAC should not require prior port disassoc
authorDane LeBlanc <leblancd@cisco.com>
Mon, 17 Nov 2014 15:40:04 +0000 (10:40 -0500)
committerDane LeBlanc <leblancd@cisco.com>
Mon, 17 Nov 2014 15:40:04 +0000 (10:40 -0500)
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

neutron/db/db_base_plugin_v2.py
neutron/plugins/ml2/plugin.py
neutron/tests/unit/test_db_plugin.py

index 70827c58afdf73c5b95422696503b7b4ac4a7579..903332de0aabcafd14a3081a985e94dabf68ea9a 100644 (file)
@@ -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:
index 0f596a66c2216ead6d6a9f88b4ff58a44779f64f..09c6e95239b76675205696ecd055043e36964b2b 100644 (file)
@@ -31,6 +31,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
@@ -729,7 +730,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"))
index 8a4f3ec5acd9094ea00efd8c3724293e106faed2..689e3ce56dae6403a0b560bf4b41a517da3ee34c 100644 (file)
@@ -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'