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