Merge "Remove FIP agent's gw port when L3 agent is deleted"
This commit is contained in:
commit
2692953017
@ -267,6 +267,9 @@ class AgentDbMixin(ext_agent.AgentPluginBase, AgentAvailabilityZoneMixin):
|
||||
payload=events.DBEventPayload(
|
||||
context, states=(agent,), resource_id=id))
|
||||
agent.delete()
|
||||
registry.publish(resources.AGENT, events.AFTER_DELETE, self,
|
||||
payload=events.DBEventPayload(
|
||||
context, states=(agent,), resource_id=id))
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def update_agent(self, context, id, agent):
|
||||
|
@ -52,6 +52,7 @@ from neutron.ipam import utils as ipam_utils
|
||||
from neutron.objects import agent as ag_obj
|
||||
from neutron.objects import base as base_obj
|
||||
from neutron.objects import l3agent as rb_obj
|
||||
from neutron.objects import ports as port_obj
|
||||
from neutron.objects import router as l3_obj
|
||||
|
||||
|
||||
@ -791,6 +792,61 @@ class DVRResourceOperationHandler(object):
|
||||
p['id'],
|
||||
l3_port_check=False)
|
||||
|
||||
def _get_ext_nets_by_host(self, context, host):
|
||||
ext_nets = set()
|
||||
ports_ids = port_obj.Port.get_ports_by_host(context, host)
|
||||
for port_id in ports_ids:
|
||||
fips = self._get_floatingips_by_port_id(context, port_id)
|
||||
for fip in fips:
|
||||
ext_nets.add(fip.floating_network_id)
|
||||
return ext_nets
|
||||
|
||||
@registry.receives(resources.AGENT, [events.AFTER_CREATE])
|
||||
def create_fip_agent_gw_ports(self, resource, event,
|
||||
trigger, payload=None):
|
||||
"""Create floating agent gw ports for DVR L3 agent.
|
||||
|
||||
Create floating IP Agent gateway ports when an L3 agent is created.
|
||||
"""
|
||||
if not payload:
|
||||
return
|
||||
agent = payload.latest_state
|
||||
if agent.get('agent_type') != const.AGENT_TYPE_L3:
|
||||
return
|
||||
# NOTE(slaweq) agent is passed in payload as dict so to avoid getting
|
||||
# again from db, lets just get configuration from this dict directly
|
||||
l3_agent_mode = agent.get('configurations', {}).get('agent_mode')
|
||||
if l3_agent_mode not in [const.L3_AGENT_MODE_DVR,
|
||||
const.L3_AGENT_MODE_DVR_SNAT]:
|
||||
return
|
||||
|
||||
host = agent['host']
|
||||
context = payload.context.elevated()
|
||||
for ext_net in self._get_ext_nets_by_host(context, host):
|
||||
self.create_fip_agent_gw_port_if_not_exists(
|
||||
context, ext_net, host)
|
||||
|
||||
@registry.receives(resources.AGENT, [events.AFTER_DELETE])
|
||||
def delete_fip_agent_gw_ports(self, resource, event,
|
||||
trigger, payload=None):
|
||||
"""Delete floating agent gw ports for DVR.
|
||||
|
||||
Delete floating IP Agent gateway ports from host when an L3 agent is
|
||||
deleted.
|
||||
"""
|
||||
if not payload:
|
||||
return
|
||||
agent = payload.latest_state
|
||||
if agent.get('agent_type') != const.AGENT_TYPE_L3:
|
||||
return
|
||||
if self._get_agent_mode(agent) not in [const.L3_AGENT_MODE_DVR,
|
||||
const.L3_AGENT_MODE_DVR_SNAT]:
|
||||
return
|
||||
|
||||
agent_gw_ports = self._get_agent_gw_ports(payload.context, agent['id'])
|
||||
for gw_port in agent_gw_ports:
|
||||
self._core_plugin.delete_port(payload.context, gw_port['id'])
|
||||
|
||||
|
||||
class _DVRAgentInterfaceMixin(object):
|
||||
"""Contains calls made by the DVR scheduler and RPC interface.
|
||||
@ -1067,6 +1123,14 @@ class _DVRAgentInterfaceMixin(object):
|
||||
if ports:
|
||||
return ports[0]
|
||||
|
||||
def _get_agent_gw_ports(self, context, agent_id):
|
||||
"""Return agent gw ports."""
|
||||
filters = {
|
||||
'device_id': [agent_id],
|
||||
'device_owner': [const.DEVICE_OWNER_AGENT_GW]
|
||||
}
|
||||
return self._core_plugin.get_ports(context, filters)
|
||||
|
||||
def check_for_fip_and_create_agent_gw_port_on_host_if_not_exists(
|
||||
self, context, port, host):
|
||||
"""Create fip agent_gw_port on host if not exists"""
|
||||
|
@ -639,6 +639,14 @@ class Port(base.NeutronDbObject):
|
||||
~models_v2.Port.device_owner.in_(excluded_device_owners))
|
||||
return [port_binding['port_id'] for port_binding in query.all()]
|
||||
|
||||
@classmethod
|
||||
def get_ports_by_host(cls, context, host):
|
||||
query = context.session.query(models_v2.Port.id).join(
|
||||
ml2_models.PortBinding)
|
||||
query = query.filter(
|
||||
ml2_models.PortBinding.host == host)
|
||||
return [port_id[0] for port_id in query.all()]
|
||||
|
||||
@classmethod
|
||||
def get_ports_by_binding_type_and_host(cls, context,
|
||||
binding_type, host):
|
||||
|
@ -37,6 +37,7 @@ from neutron.db.models import l3 as l3_models
|
||||
from neutron.db import models_v2
|
||||
from neutron.objects import agent as agent_obj
|
||||
from neutron.objects import l3agent as rb_obj
|
||||
from neutron.objects import ports as port_obj
|
||||
from neutron.objects import router as router_obj
|
||||
from neutron.tests.unit.db import test_db_base_plugin_v2
|
||||
from neutron.tests.unit.extensions import test_l3
|
||||
@ -815,6 +816,115 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
|
||||
fip, floatingip, router))
|
||||
self.assertFalse(create_fip.called)
|
||||
|
||||
def test_get_ext_nets_by_host(self):
|
||||
ports = [mock.Mock(id=_uuid()) for _ in range(3)]
|
||||
fips = [mock.Mock(fixed_port_id=p.id, floating_network_id=_uuid())
|
||||
for p in ports]
|
||||
expected_ext_nets = set([fip.floating_network_id for fip in fips])
|
||||
with mock.patch.object(
|
||||
port_obj.Port, 'get_ports_by_host',
|
||||
return_value=[p.id for p in ports]
|
||||
) as get_ports_by_host, mock.patch.object(
|
||||
self.mixin, '_get_floatingips_by_port_id', return_value=fips
|
||||
) as get_floatingips_by_port_id:
|
||||
self.assertEqual(
|
||||
expected_ext_nets,
|
||||
self.mixin._get_ext_nets_by_host(self.ctx, 'host'))
|
||||
get_ports_by_host.assert_called_once_with(self.ctx, 'host')
|
||||
get_floatingips_by_port_id.assert_has_calls(
|
||||
[mock.call(self.ctx, p.id) for p in ports])
|
||||
|
||||
def _test_create_fip_agent_gw_ports(self, agent_type, agent_mode=None):
|
||||
agent = {
|
||||
'id': _uuid(),
|
||||
'host': 'host',
|
||||
'agent_type': agent_type,
|
||||
'configurations': {'agent_mode': agent_mode}}
|
||||
payload = events.DBEventPayload(
|
||||
self.ctx, states=(agent,), resource_id=agent['id'])
|
||||
|
||||
ext_nets = ['ext-net-1', 'ext-net-2']
|
||||
with mock.patch.object(
|
||||
self.mixin,
|
||||
'create_fip_agent_gw_port_if_not_exists'
|
||||
) as create_fip_gw, mock.patch.object(
|
||||
self.mixin, "_get_ext_nets_by_host",
|
||||
return_value=ext_nets
|
||||
) as get_ext_nets_by_host:
|
||||
|
||||
registry.publish(resources.AGENT, events.AFTER_CREATE, mock.Mock(),
|
||||
payload=payload)
|
||||
|
||||
if agent_type == 'L3 agent' and agent_mode in ['dvr', 'dvr_snat']:
|
||||
get_ext_nets_by_host.assert_called_once_with(
|
||||
mock.ANY, 'host')
|
||||
create_fip_gw.assert_has_calls(
|
||||
[mock.call(mock.ANY, ext_net, 'host') for
|
||||
ext_net in ext_nets])
|
||||
else:
|
||||
get_ext_nets_by_host.assert_not_called()
|
||||
create_fip_gw.assert_not_called()
|
||||
|
||||
def test_create_fip_agent_gw_ports(self):
|
||||
self._test_create_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr')
|
||||
self._test_create_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr_snat')
|
||||
|
||||
def test_create_fip_agent_gw_ports_dvr_no_external_agent(self):
|
||||
self._test_create_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr_no_external')
|
||||
|
||||
def test_create_fip_agent_gw_ports_non_dvr_agent(self):
|
||||
self._test_create_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='legacy')
|
||||
|
||||
def test_create_fip_agent_gw_ports_deleted_non_l3_agent(self):
|
||||
self._test_create_fip_agent_gw_ports('Other agent type')
|
||||
|
||||
def _test_delete_fip_agent_gw_ports(self, agent_type, agent_mode=None):
|
||||
agent = agent_obj.Agent(
|
||||
self.ctx, id=_uuid(), agent_type=agent_type,
|
||||
configurations={"agent_mode": agent_mode})
|
||||
payload = events.DBEventPayload(
|
||||
self.ctx, states=(agent,), resource_id=agent.id)
|
||||
|
||||
gw_port = {'id': _uuid(), 'network_id': _uuid()}
|
||||
with mock.patch.object(
|
||||
self.mixin, '_get_agent_gw_ports',
|
||||
return_value=[gw_port]
|
||||
) as get_agent_gw_ports, mock.patch.object(
|
||||
self.core_plugin, 'delete_port'
|
||||
) as delete_port:
|
||||
registry.publish(resources.AGENT, events.AFTER_DELETE, mock.Mock(),
|
||||
payload=payload)
|
||||
|
||||
if agent_type == 'L3 agent' and agent_mode in ['dvr', 'dvr_snat']:
|
||||
get_agent_gw_ports.assert_called_once_with(payload.context,
|
||||
agent['id'])
|
||||
delete_port.assert_called_once_with(payload.context,
|
||||
gw_port['id'])
|
||||
else:
|
||||
get_agent_gw_ports.assert_not_called()
|
||||
delete_port.assert_not_called()
|
||||
|
||||
def test_delete_fip_agent_gw_ports(self):
|
||||
self._test_delete_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr')
|
||||
self._test_delete_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr_snat')
|
||||
|
||||
def test_delete_fip_agent_gw_ports_dvr_no_external_agent(self):
|
||||
self._test_delete_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='dvr_no_external')
|
||||
|
||||
def test_delete_fip_agent_gw_ports_non_dvr_agent(self):
|
||||
self._test_delete_fip_agent_gw_ports(
|
||||
agent_type='L3 agent', agent_mode='legacy')
|
||||
|
||||
def test_delete_fip_agent_gw_ports_deleted_non_l3_agent(self):
|
||||
self._test_delete_fip_agent_gw_ports('Other agent type')
|
||||
|
||||
def _test_update_router_gw_info_external_network_change(self):
|
||||
router_dict = {'name': 'test_router', 'admin_state_up': True,
|
||||
'distributed': True}
|
||||
|
Loading…
Reference in New Issue
Block a user