diff --git a/neutron/db/ipam_backend_mixin.py b/neutron/db/ipam_backend_mixin.py index 8b9fad0549e..68f465461d7 100644 --- a/neutron/db/ipam_backend_mixin.py +++ b/neutron/db/ipam_backend_mixin.py @@ -312,7 +312,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): pool_2=r_range, subnet_cidr=subnet_cidr) - def _validate_segment(self, context, network_id, segment_id, action=None): + def _validate_segment(self, context, network_id, segment_id, action=None, + old_segment_id=None): query = context.session.query(models_v2.Subnet.segment_id) query = query.filter(models_v2.Subnet.network_id == network_id) associated_segments = set(row.segment_id for row in query) @@ -320,7 +321,7 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): raise segment_exc.SubnetsNotAllAssociatedWithSegments( network_id=network_id) - if action == 'update': + if action == 'update' and old_segment_id != segment_id: # Check the current state of segments and subnets on the network # before allowing migration from non-routed to routed network. if query.count() > 1: diff --git a/neutron/db/ipam_pluggable_backend.py b/neutron/db/ipam_pluggable_backend.py index 24387df216d..4a0c802a91a 100644 --- a/neutron/db/ipam_pluggable_backend.py +++ b/neutron/db/ipam_pluggable_backend.py @@ -428,9 +428,12 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin): port['fixed_ips']) def update_db_subnet(self, context, id, s, old_pools): + subnet = obj_subnet.Subnet.get_object(context, id=id) + old_segment_id = subnet.segment_id if subnet else None if 'segment_id' in s: - self._validate_segment(context, s['network_id'], s['segment_id'], - action='update') + self._validate_segment( + context, s['network_id'], s['segment_id'], action='update', + old_segment_id=old_segment_id) # 'allocation_pools' is removed from 's' in # _update_subnet_allocation_pools (ipam_backend_mixin), # so create unchanged copy for ipam driver diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index 383cb32e16d..c56e9ba8bc4 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -540,6 +540,27 @@ class TestSegmentSubnetAssociation(SegmentTestCase): self.assertEqual(webob.exc.HTTPOk.code, response.status_int) self.assertEqual(res['subnet']['segment_id'], segment['id']) + def test_update_subnet_with_current_segment_id(self): + with self.network() as network: + net = network['network'] + + segment1 = self._test_create_segment(network_id=net['id'], + physical_network='phys_net1', + segmentation_id=200)['segment'] + self._test_create_segment(network_id=net['id'], + physical_network='phys_net2', + segmentation_id=200)['segment'] + with self.subnet(network=network, segment_id=segment1['id']) as subnet: + subnet = subnet['subnet'] + + data = {'subnet': {'segment_id': segment1['id']}} + request = self.new_update_request('subnets', data, subnet['id']) + response = request.get_response(self.api) + res = self.deserialize(self.fmt, response) + + self.assertEqual(webob.exc.HTTPOk.code, response.status_int) + self.assertEqual(res['subnet']['segment_id'], segment1['id']) + def test_associate_existing_subnet_fail_if_multiple_segments(self): with self.network() as network: net = network['network']