diff --git a/neutron/services/segments/plugin.py b/neutron/services/segments/plugin.py index 3c0d098e9c8..da51a2cac59 100644 --- a/neutron/services/segments/plugin.py +++ b/neutron/services/segments/plugin.py @@ -14,9 +14,11 @@ # License for the specific language governing permissions and limitations # under the License. +from sqlalchemy.orm import session from neutron.api.v2 import attributes from neutron.db import common_db_mixin +from neutron.db import models_v2 from neutron.extensions import ip_allocation from neutron.extensions import segment from neutron import manager @@ -31,10 +33,16 @@ def _extend_port_dict_binding(plugin, port_res, port_db): if not manager.NeutronManager.get_service_plugins().get('segments'): return - if port_res.get('fixed_ips'): - value = ip_allocation.IP_ALLOCATION_IMMEDIATE - else: - value = ip_allocation.IP_ALLOCATION_DEFERRED + value = ip_allocation.IP_ALLOCATION_IMMEDIATE + if not port_res.get('fixed_ips'): + # NOTE Only routed network ports have deferred allocation. Check if it + # is routed by looking for subnets associated with segments. + object_session = session.Session.object_session(port_db) + query = object_session.query(models_v2.Subnet) + query = query.filter_by(network_id=port_db.network_id) + query = query.filter(models_v2.Subnet.segment_id.isnot(None)) + if query.count(): + value = ip_allocation.IP_ALLOCATION_DEFERRED port_res[ip_allocation.IP_ALLOCATION] = value diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index efe092e92c6..1dde9f0c097 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -830,6 +830,24 @@ class TestSegmentAwareIpam(SegmentTestCase): # Gets bad request because there are no eligible subnets. self.assertEqual(webob.exc.HTTPBadRequest.code, response.status_int) + def test_port_without_ip_not_deferred(self): + """Ports without addresses on non-routed networks are not deferred""" + with self.network() as network: + pass + + # Create a bound port with no IP address (since there is no subnet) + response = self._create_port(self.fmt, + net_id=network['network']['id'], + tenant_id=network['network']['tenant_id'], + arg_list=(portbindings.HOST_ID,), + **{portbindings.HOST_ID: 'fakehost'}) + port = self.deserialize(self.fmt, response) + request = self.new_show_request('ports', port['port']['id']) + response = self.deserialize(self.fmt, request.get_response(self.api)) + + self.assertEqual(ip_allocation.IP_ALLOCATION_IMMEDIATE, + response['port'][ip_allocation.IP_ALLOCATION]) + def test_port_update_is_host_aware(self): """Binding information is provided, subnets on segments""" with self.network() as network: @@ -847,11 +865,11 @@ class TestSegmentAwareIpam(SegmentTestCase): arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: 'fakehost'}) port = self.deserialize(self.fmt, response) - self._validate_deferred_ip_allocation(port['port']['id']) # Create the subnet and try to update the port to get an IP with self.subnet(network=network, segment_id=segment['segment']['id']) as subnet: + self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': { 'fixed_ips': [{'subnet_id': subnet['subnet']['id']}]}} @@ -889,8 +907,6 @@ class TestSegmentAwareIpam(SegmentTestCase): ips = port['port']['fixed_ips'] self.assertEqual(0, len(ips)) - self._validate_deferred_ip_allocation(port['port']['id']) - return port def test_port_update_deferred_allocation(self): @@ -901,6 +917,7 @@ class TestSegmentAwareIpam(SegmentTestCase): self._setup_host_mappings([(segment['segment']['id'], 'fakehost')]) port = self._create_deferred_ip_port(network) + self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost'}} @@ -980,6 +997,7 @@ class TestSegmentAwareIpam(SegmentTestCase): network, segment, subnet = self._create_test_segment_with_subnet() port = self._create_deferred_ip_port(network) + self._validate_deferred_ip_allocation(port['port']['id']) # Try requesting an IP (but the only subnet is on a segment) data = {'port': {portbindings.HOST_ID: 'fakehost'}} @@ -998,6 +1016,7 @@ class TestSegmentAwareIpam(SegmentTestCase): network, segments, _s = self._create_test_segments_with_subnets(2) port = self._create_deferred_ip_port(network) + self._validate_deferred_ip_allocation(port['port']['id']) # This host is bound to multiple segments self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost'),