From a59bc98edd0b86705ee3c6b7098bbe242c583316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Mon, 16 Sep 2019 21:52:51 +0200 Subject: [PATCH] fixed_configured=True when Add/Remove port IPs When updating a port with the fixed_ips request the fixed_configured argument should be set to true when calling _ipam_get_subnets() so that all subnets are returned if host is not set. Otherwise the ip allocation will be deffered and an empty list of possible subnets for the port is returned. Which in turn led to raising an error that the network requires subnets to allocate an IP address. Closes-Bug: #1844124 Change-Id: I2e690ea0cf5fa0614e39be2b0e83afad3daa7f48 (cherry picked from commit def8e95aad1e4588c369d537ee66234245eefdf6) --- neutron/db/ipam_pluggable_backend.py | 2 +- .../unit/db/test_ipam_pluggable_backend.py | 4 +-- neutron/tests/unit/extensions/test_segment.py | 32 +++++++++++++++++-- ...ed-provider-networks-c54a54844d9a3926.yaml | 7 ++++ 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yaml diff --git a/neutron/db/ipam_pluggable_backend.py b/neutron/db/ipam_pluggable_backend.py index bc65e7c542a..7282ef80ed2 100644 --- a/neutron/db/ipam_pluggable_backend.py +++ b/neutron/db/ipam_pluggable_backend.py @@ -345,7 +345,7 @@ class IpamPluggableBackend(ipam_backend_mixin.IpamBackendMixin): try: subnets = self._ipam_get_subnets( context, network_id=port['network_id'], host=host, - service_type=port.get('device_owner')) + service_type=port.get('device_owner'), fixed_configured=True) except ipam_exc.DeferIpam: subnets = [] diff --git a/neutron/tests/unit/db/test_ipam_pluggable_backend.py b/neutron/tests/unit/db/test_ipam_pluggable_backend.py index 9269852b6be..c013176e471 100644 --- a/neutron/tests/unit/db/test_ipam_pluggable_backend.py +++ b/neutron/tests/unit/db/test_ipam_pluggable_backend.py @@ -688,8 +688,8 @@ class TestDbBasePluginIpam(test_db_base.NeutronDbPluginV2TestCase): original_ips, new_ips, mac) mocks['driver'].get_address_request_factory.assert_called_once_with() mocks['ipam']._ipam_get_subnets.assert_called_once_with( - context, network_id=port_dict['network_id'], host=None, - service_type=port_dict['device_owner']) + context, network_id=port_dict['network_id'], fixed_configured=True, + host=None, service_type=port_dict['device_owner']) # Validate port_dict is passed into address_factory address_factory.get_request.assert_called_once_with(context, port_dict, diff --git a/neutron/tests/unit/extensions/test_segment.py b/neutron/tests/unit/extensions/test_segment.py index 1b99bc154ef..db16dd4818f 100644 --- a/neutron/tests/unit/extensions/test_segment.py +++ b/neutron/tests/unit/extensions/test_segment.py @@ -1175,8 +1175,8 @@ class TestSegmentAwareIpam(SegmentAwareIpamTestCase): self.assertEqual(segment_exc.HostConnectedToMultipleSegments.__name__, res['NeutronError']['type']) - def test_port_update_excludes_hosts_on_segments(self): - """No binding information is provided, subnets on segments""" + def test_port_update_with_fixed_ips_ok_if_no_binding_host(self): + """No binding host information is provided, subnets on segments""" with self.network() as network: segment = self._test_create_segment( network_id=network['network']['id'], @@ -1196,7 +1196,33 @@ class TestSegmentAwareIpam(SegmentAwareIpamTestCase): port_req = self.new_update_request('ports', data, port_id) response = port_req.get_response(self.api) - # Gets bad request because there are no eligible subnets. + # The IP is allocated since there is no binding host info any + # subnet can be used for allocation. + self.assertEqual(webob.exc.HTTPOk.code, response.status_int) + + def test_port_update_with_fixed_ips_fail_if_host_not_on_segment(self): + """Binding information is provided, subnets on segments. Update to + subnet on different segment fails. + """ + network, segments, subnets = self._create_test_segments_with_subnets(2) + + # Setup host mappings + self._setup_host_mappings([(segments[0]['segment']['id'], 'fakehost')]) + + # Create a port and validate immediate ip allocation + res = self._create_port_and_show(network, + arg_list=(portbindings.HOST_ID,), + **{portbindings.HOST_ID: 'fakehost'}) + self._validate_immediate_ip_allocation(res['port']['id']) + + # Try requesting an new IP, but the subnet does not match host segment + port_id = res['port']['id'] + data = {'port': { + 'fixed_ips': [{'subnet_id': subnets[1]['subnet']['id']}]}} + port_req = self.new_update_request('ports', data, port_id) + response = port_req.get_response(self.api) + + # Port update fails. self.assertEqual(webob.exc.HTTPBadRequest.code, response.status_int) def _create_port_and_show(self, network, **kwargs): diff --git a/releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yaml b/releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yaml new file mode 100644 index 00000000000..a0320ed7ba0 --- /dev/null +++ b/releasenotes/notes/fix-update-port-fixed-ips-on-routed-provider-networks-c54a54844d9a3926.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + When updating the fixed-ips of a port residing on a routed provider + network the port update would always fail if *host* was not set. + See bug: `1844124 `_. +