from oslo_db import exception as db_exc
from oslo_log import log
from six import moves
+from sqlalchemy import or_
from neutron.common import exceptions as exc
from neutron.common import topics
param ip: the IP address of the endpoint
"""
+ @abc.abstractmethod
+ def delete_endpoint_by_host_or_ip(self, host, ip):
+ """Delete the endpoint in the type_driver database.
+
+ This function will delete any endpoint matching the specified
+ ip or host.
+
+ param host: the host name of the endpoint
+ param ip: the IP address of the endpoint
+ """
+
def _initialize(self, raw_tunnel_ranges):
self.tunnel_ranges = []
self._parse_tunnel_ranges(raw_tunnel_ranges, self.tunnel_ranges)
(session.query(self.endpoint_model).
filter_by(ip_address=ip).delete())
+ def delete_endpoint_by_host_or_ip(self, host, ip):
+ LOG.debug("delete_endpoint_by_host_or_ip() called for "
+ "host %(host)s or %(ip)s", {'host': host, 'ip': ip})
+ session = db_api.get_session()
+ with session.begin(subtransactions=True):
+ session.query(self.endpoint_model).filter(
+ or_(self.endpoint_model.host == host,
+ self.endpoint_model.ip_address == ip)).delete()
+
def _get_endpoints(self):
LOG.debug("_get_endpoints() called")
session = db_api.get_session()
# found, delete the endpoint belonging to that host and
# add endpoint with latest (tunnel_ip, host), it is a case
# where local_ip of an agent got changed.
+ # 5. If the passed host had another ip in the DB the host-id has
+ # roamed to a different IP then delete any reference to the new
+ # local_ip or the host id. Don't notify tunnel_delete for the
+ # old IP since that one could have been taken by a different
+ # agent host-id (neutron-ovs-cleanup should be used to clean up
+ # the stale endpoints).
+ # Finally create a new endpoint for the (tunnel_ip, host).
if host:
host_endpoint = driver.obj.get_endpoint_by_host(host)
ip_endpoint = driver.obj.get_endpoint_by_ip(tunnel_ip)
and host_endpoint is None):
driver.obj.delete_endpoint(ip_endpoint.ip_address)
elif (ip_endpoint and ip_endpoint.host != host):
- msg = (_("Tunnel IP %(ip)s in use with host %(host)s"),
- {'ip': ip_endpoint.ip_address,
- 'host': ip_endpoint.host})
- raise exc.InvalidInput(error_message=msg)
+ LOG.info(
+ _LI("Tunnel IP %(ip)s was used by host %(host)s and "
+ "will be assigned to %(new_host)s"),
+ {'ip': ip_endpoint.ip_address,
+ 'host': ip_endpoint.host,
+ 'new_host': host})
+ driver.obj.delete_endpoint_by_host_or_ip(
+ host, ip_endpoint.ip_address)
elif (host_endpoint and host_endpoint.ip_address != tunnel_ip):
# Notify all other listening agents to delete stale tunnels
self._notifier.tunnel_delete(rpc_context,
'host': HOST_ONE}
self._test_tunnel_sync(kwargs, True)
- def test_tunnel_sync_called_with_used_tunnel_ip_case_one(self):
+ def test_tunnel_sync_called_with_used_tunnel_ip_host_roaming(self):
self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE)
kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
'host': HOST_TWO}
- self._test_tunnel_sync_raises(kwargs)
+ self._test_tunnel_sync(kwargs, False)
- def test_tunnel_sync_called_with_used_tunnel_ip_case_two(self):
+ def test_tunnel_sync_called_with_used_tunnel_ip_roaming_case_two(self):
self.driver.add_endpoint(TUNNEL_IP_ONE, None)
self.driver.add_endpoint(TUNNEL_IP_TWO, HOST_TWO)
kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
'host': HOST_TWO}
- self._test_tunnel_sync_raises(kwargs)
+ self._test_tunnel_sync(kwargs, False)
def test_tunnel_sync_called_without_tunnel_ip(self):
kwargs = {'tunnel_type': self.TYPE, 'host': None}