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 0d4889df41
)
This commit is contained in:
parent
fb3f718061
commit
5b7a1f85b3
@ -692,7 +692,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
|||||||
# subnets. Happens on routed networks when host isn't known.
|
# subnets. Happens on routed networks when host isn't known.
|
||||||
raise ipam_exceptions.DeferIpam()
|
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,
|
def _find_candidate_subnets(self, context, network_id, host, service_type,
|
||||||
fixed_configured):
|
fixed_configured):
|
||||||
@ -773,7 +774,8 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
|
|||||||
new_host_requested = host and host != old_host
|
new_host_requested = host and host != old_host
|
||||||
if old_ips and new_host_requested and not fixed_ips_requested:
|
if old_ips and new_host_requested and not fixed_ips_requested:
|
||||||
valid_subnets = self._ipam_get_subnets(
|
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}
|
valid_subnet_ids = {s['id'] for s in valid_subnets}
|
||||||
for fixed_ip in old_ips:
|
for fixed_ip in old_ips:
|
||||||
if fixed_ip['subnet_id'] not in valid_subnet_ids:
|
if fixed_ip['subnet_id'] not in valid_subnet_ids:
|
||||||
|
@ -69,7 +69,8 @@ class IpAddressGenerationFailureAllSubnets(IpAddressGenerationFailure):
|
|||||||
|
|
||||||
|
|
||||||
class IpAddressGenerationFailureNoMatchingSubnet(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):
|
class IPAllocationFailed(exceptions.NeutronException):
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
from neutron_lib.api.definitions import portbindings
|
||||||
|
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron.db import subnet_service_type_db_models
|
from neutron.db import subnet_service_type_db_models
|
||||||
from neutron.extensions import subnet_service_types
|
from neutron.extensions import subnet_service_types
|
||||||
@ -40,7 +42,7 @@ class SubnetServiceTypesExtensionTestPlugin(
|
|||||||
"""Test plugin to mixin the subnet service_types extension.
|
"""Test plugin to mixin the subnet service_types extension.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
supported_extension_aliases = ["subnet-service-types"]
|
supported_extension_aliases = ["subnet-service-types", "binding"]
|
||||||
|
|
||||||
|
|
||||||
class SubnetServiceTypesExtensionTestCase(
|
class SubnetServiceTypesExtensionTestCase(
|
||||||
@ -324,6 +326,29 @@ class SubnetServiceTypesExtensionTestCase(
|
|||||||
# applied
|
# applied
|
||||||
port = self._update('ports', port['id'], data)
|
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(
|
class SubnetServiceTypesExtensionTestCasev6(
|
||||||
SubnetServiceTypesExtensionTestCase):
|
SubnetServiceTypesExtensionTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user