File 0001-Always-pass-device_owner-to-_ipam_get_subnets.patch of Package openstack-neutron

From 7ff7b16adb3e1432d926cfbc3684b23359c716d6 Mon Sep 17 00:00:00 2001
From: Brian Haley <bhaley@redhat.com>
Date: Mon, 12 Feb 2018 15:38:24 -0500
Subject: [PATCH] Always pass device_owner to _ipam_get_subnets()

There was one code path where the existing device_owner
value, which is used for subnet selection when service_type's
are in use was not passed to _imap_get_subnets().  This could
trigger one of two exceptions - HostNotCompatibleWithFixedIps()
or IpAddressGenerationFailureNoMatchingSubnet() depending
on the environment.  Pass it along if known.

Also update the IpAddressGenerationFailureNoMatchingSubnet
exception to print the network_id and service_type values since
it could aid in debugging the problem quicker.

Change-Id: Ic13901b92cac05e8ddf1092b66aa5bcc5623fa8a
Closes-bug: #1637366
(cherry picked from commit 0d4889df41485cece4bef5915753ae7efd808138)
(cherry picked from commit fb5c2ba3390668afeca828681edc54fb544cef11)
---
 neutron/db/ipam_backend_mixin.py              |  6 +++--
 neutron/ipam/exceptions.py                    |  3 ++-
 .../extensions/test_subnet_service_types.py   | 27 ++++++++++++++++++-
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/neutron/db/ipam_backend_mixin.py b/neutron/db/ipam_backend_mixin.py
index bf10e6eef1..40d6c238de 100644
--- a/neutron/db/ipam_backend_mixin.py
+++ b/neutron/db/ipam_backend_mixin.py
@@ -694,7 +694,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
             # subnets. Happens on routed networks when host isn't known.
             raise ipam_exceptions.DeferIpam()
 
-        raise ipam_exceptions.IpAddressGenerationFailureNoMatchingSubnet()
+        raise ipam_exceptions.IpAddressGenerationFailureNoMatchingSubnet(
+            network_id=network_id, service_type=service_type)
 
     def _find_candidate_subnets(self, context, network_id, host, service_type):
         """Find canditate subnets for the network, host, and service_type"""
@@ -767,7 +768,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
             new_host_requested = host and host != old_host
             if old_ips and new_host_requested and not fixed_ips_requested:
                 valid_subnets = self._ipam_get_subnets(
-                    context, old_port['network_id'], host)
+                    context, old_port['network_id'], host,
+                    service_type=old_port.get('device_owner'))
                 valid_subnet_ids = {s['id'] for s in valid_subnets}
                 for fixed_ip in old_ips:
                     if fixed_ip['subnet_id'] not in valid_subnet_ids:
diff --git a/neutron/ipam/exceptions.py b/neutron/ipam/exceptions.py
index feb93a7f80..250ca7aad2 100644
--- a/neutron/ipam/exceptions.py
+++ b/neutron/ipam/exceptions.py
@@ -69,7 +69,8 @@ class IpAddressGenerationFailureAllSubnets(IpAddressGenerationFailure):
 
 
 class IpAddressGenerationFailureNoMatchingSubnet(IpAddressGenerationFailure):
-    message = _("No valid service subnet for the given device owner.")
+    message = _("No valid service subnet for the given device owner, "
+                "network %(network_id)s, service type %(service_type)s.")
 
 
 class IPAllocationFailed(exceptions.NeutronException):
diff --git a/neutron/tests/unit/extensions/test_subnet_service_types.py b/neutron/tests/unit/extensions/test_subnet_service_types.py
index d46b690bd6..e30d08680d 100644
--- a/neutron/tests/unit/extensions/test_subnet_service_types.py
+++ b/neutron/tests/unit/extensions/test_subnet_service_types.py
@@ -12,6 +12,8 @@
 
 import webob.exc
 
+from neutron_lib.api.definitions import portbindings
+
 from neutron.db import db_base_plugin_v2
 from neutron.extensions import subnet_service_types
 from neutron.tests.unit.db import test_db_base_plugin_v2
@@ -37,7 +39,7 @@ class SubnetServiceTypesExtensionTestPlugin(
     """Test plugin to mixin the subnet service_types extension.
     """
 
-    supported_extension_aliases = ["subnet-service-types"]
+    supported_extension_aliases = ["subnet-service-types", "binding"]
 
 
 class SubnetServiceTypesExtensionTestCase(
@@ -318,6 +320,29 @@ class SubnetServiceTypesExtensionTestCase(
         # applied
         port = self._update('ports', port['id'], data)
 
+    def test_update_port_host_binding(self):
+        with self.network() as network:
+            pass
+        service_type = 'compute:foo'
+        # Create a subnet with a service_type
+        self._create_service_subnet([service_type],
+                                    cidr=self.CIDRS[1],
+                                    network=network)
+        # Create a port with a matching device owner
+        network = network['network']
+        port = self._create_port(self.fmt,
+                                 net_id=network['id'],
+                                 tenant_id=network['tenant_id'],
+                                 device_owner=service_type,
+                                 arg_list=(portbindings.HOST_ID,),
+                                 **{portbindings.HOST_ID: 'fakehost'})
+        port = self.deserialize('json', port)['port']
+        # Update the port's host binding.
+        data = {'port': {portbindings.HOST_ID: 'fakehost2'}}
+        # self._update will fail with a MismatchError if the update cannot be
+        # applied
+        port = self._update('ports', port['id'], data)
+
 
 class SubnetServiceTypesExtensionTestCasev6(
         SubnetServiceTypesExtensionTestCase):
-- 
2.19.1

openSUSE Build Service is sponsored by