Add RPC command and delete if last FIP on Agent

Today FloatingIP Agent gateway port is deleted and
re-created for DVR based routers based on floatingip
association and disassociation with VMs on compute
nodes by the plugin.

This introduces lot more strain on the plugin to
create and delete these ports when VMs come up and
get deleted that are associated with FloatingIps.

This patch will introduce an RPC call for the agent
to initiate a agent gateway port delete.

Also the agent will look for the last floatingip that
it manages, and if condition satisfies, the agent will
request the server to remove the FloatingIP Agent
Gateway port.

Change-Id: I47694b2ee60c363e2fe59ad5f7d168252da08a45
Related-Bug: #1468007
Related-Bug: #1408855
Related-Bug: #1450982
This commit is contained in:
Swaminathan Vasudevan 2015-06-22 16:50:43 -07:00
parent d5aa1659f5
commit 639f1893dd
8 changed files with 80 additions and 28 deletions

View File

@ -80,7 +80,8 @@ class L3PluginApi(object):
to update_ha_routers_states
1.5 - Added update_ha_routers_states
1.6 - Added process_prefix_update
1.7 - DVR support: new L3 plugin methods added.
- delete_agent_gateway_port
"""
def __init__(self, topic, host):
@ -139,6 +140,12 @@ class L3PluginApi(object):
return cctxt.call(context, 'process_prefix_update',
subnets=prefix_update)
def delete_agent_gateway_port(self, context, fip_net):
"""Delete Floatingip_agent_gateway_port."""
cctxt = self.client.prepare(version='1.7')
return cctxt.call(context, 'delete_agent_gateway_port',
host=self.host, network_id=fip_net)
class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
ha.AgentMixin,

View File

@ -137,6 +137,17 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
# destroying it. The two could end up conflicting on
# creating/destroying interfaces and such. I think I'd like a
# semaphore to sync creation/deletion of this namespace.
# NOTE (Swami): Since we are deleting the namespace here we
# should be able to delete the floatingip agent gateway port
# for the provided external net since we don't need it anymore.
if self.fip_ns.agent_gateway_port:
LOG.debug('Removed last floatingip, so requesting the '
'server to delete Floatingip Agent Gateway port:'
'%s', self.fip_ns.agent_gateway_port)
self.agent.plugin_rpc.delete_agent_gateway_port(
self.agent.context,
self.fip_ns.agent_gateway_port['network_id'])
self.fip_ns.delete()
self.fip_ns = None

View File

@ -45,7 +45,8 @@ class L3RpcCallback(object):
# since it was unused. The RPC version was not changed
# 1.5 Added update_ha_routers_states
# 1.6 Added process_prefix_update to support IPv6 Prefix Delegation
target = oslo_messaging.Target(version='1.6')
# 1.7 Added method delete_agent_gateway_port for DVR Routers
target = oslo_messaging.Target(version='1.7')
@property
def plugin(self):
@ -281,3 +282,11 @@ class L3RpcCallback(object):
subnet_id,
{'subnet': {'cidr': prefix}}))
return updated_subnets
def delete_agent_gateway_port(self, context, **kwargs):
"""Delete Floatingip agent gateway port."""
network_id = kwargs.get('network_id')
host = kwargs.get('host')
admin_ctx = neutron_context.get_admin_context()
self.l3plugin.delete_floatingip_agent_gateway_port(
admin_ctx, network_id, host_id=host)

View File

@ -170,7 +170,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
ext_net_gw_ports = self._core_plugin.get_ports(
context.elevated(), filters)
if not ext_net_gw_ports:
self._delete_floatingip_agent_gateway_port(
self.delete_floatingip_agent_gateway_port(
context.elevated(), None, gw_ext_net_id)
def _create_gw_port(self, context, router_id, router, new_network,
@ -265,7 +265,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
context, fip_hostid, floatingip_db['floating_network_id']):
LOG.debug('Deleting the Agent GW Port for ext-net: '
'%s', floatingip_db['floating_network_id'])
self._delete_floatingip_agent_gateway_port(
self.delete_floatingip_agent_gateway_port(
context, fip_hostid, floatingip_db['floating_network_id'])
def delete_floatingip(self, context, id):
@ -553,7 +553,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
return True
return False
def _delete_floatingip_agent_gateway_port(
def delete_floatingip_agent_gateway_port(
self, context, host_id, ext_net_id):
"""Function to delete FIP gateway port with given ext_net_id."""
# delete any fip agent gw port

View File

@ -1460,3 +1460,27 @@ class TestDvrRouter(L3AgentTestFramework):
router_updated, internal_dev_name=internal_device_name)
self.assertFalse(sg_device)
self.assertTrue(qg_device)
def test_dvr_router_calls_delete_agent_gateway_if_last_fip(self):
"""Test to validate delete fip if it is last fip managed by agent."""
self.agent.conf.agent_mode = 'dvr_snat'
router_info = self.generate_dvr_router_info(enable_snat=True)
router1 = self.manage_router(self.agent, router_info)
floating_agent_gw_port = (
router1.router[l3_constants.FLOATINGIP_AGENT_INTF_KEY])
self.assertTrue(floating_agent_gw_port)
fip_ns = router1.fip_ns.get_name()
router1.fip_ns.agent_gw_port = floating_agent_gw_port
self.assertTrue(self._namespace_exists(router1.ns_name))
self.assertTrue(self._namespace_exists(fip_ns))
self._assert_dvr_floating_ips(router1)
self._assert_dvr_snat_gateway(router1)
router1.router[l3_constants.FLOATINGIP_KEY] = []
rpc_mock = mock.patch.object(
self.agent.plugin_rpc, 'delete_agent_gateway_port').start()
self.agent._process_updated_router(router1.router)
self.assertTrue(rpc_mock.called)
rpc_mock.assert_called_once_with(
self.agent.context,
floating_agent_gw_port[0]['network_id'])
self.assertFalse(self._namespace_exists(fip_ns))

View File

@ -142,7 +142,7 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework):
network_id, port = (
self.setup_create_agent_gw_port_for_network())
self.l3_plugin._delete_floatingip_agent_gateway_port(
self.l3_plugin.delete_floatingip_agent_gateway_port(
self.context, "", network_id)
self.assertIsNone(
self.l3_plugin._get_agent_gw_ports_exist_for_network(

View File

@ -145,27 +145,27 @@ class TestDvrRouterOperations(base.BaseTestCase):
'interface_driver': self.mock_driver}
def _create_router(self, router=None, **kwargs):
agent_conf = mock.Mock()
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
self.router_id = _uuid()
if not router:
router = mock.MagicMock()
return dvr_router.DvrLocalRouter(mock.sentinel.agent,
mock.sentinel.myhost,
return dvr_router.DvrLocalRouter(agent,
HOSTNAME,
self.router_id,
router,
agent_conf,
mock.sentinel.interface_driver,
self.conf,
mock.Mock(),
**kwargs)
def test_get_floating_ips_dvr(self):
router = mock.MagicMock()
router.get.return_value = [{'host': mock.sentinel.myhost},
router.get.return_value = [{'host': HOSTNAME},
{'host': mock.sentinel.otherhost}]
ri = self._create_router(router)
fips = ri.get_floating_ips()
self.assertEqual([{'host': mock.sentinel.myhost}], fips)
self.assertEqual([{'host': HOSTNAME}], fips)
@mock.patch.object(ip_lib, 'send_ip_addr_adv_notif')
@mock.patch.object(ip_lib, 'IPDevice')
@ -242,15 +242,16 @@ class TestDvrRouterOperations(base.BaseTestCase):
ri.rtr_fip_subnet = lla.LinkLocalAddressPair('15.1.2.3/32')
_, fip_to_rtr = ri.rtr_fip_subnet.get_pair()
fip_ns = ri.fip_ns
ri.floating_ip_removed_dist(fip_cidr)
self.assertTrue(fip_ns.destroyed)
mIPWrapper().del_veth.assert_called_once_with(
fip_ns.get_int_device_name(router['id']))
mIPDevice().route.delete_gateway.assert_called_once_with(
str(fip_to_rtr.ip), table=16)
fip_ns.unsubscribe.assert_called_once_with(ri.router_id)
with mock.patch.object(self.plugin_api,
'delete_agent_gateway_port') as del_fip_gw:
ri.floating_ip_removed_dist(fip_cidr)
self.assertTrue(del_fip_gw.called)
self.assertTrue(fip_ns.destroyed)
mIPWrapper().del_veth.assert_called_once_with(
fip_ns.get_int_device_name(router['id']))
mIPDevice().route.delete_gateway.assert_called_once_with(
str(fip_to_rtr.ip), table=16)
fip_ns.unsubscribe.assert_called_once_with(ri.router_id)
def _test_add_floating_ip(self, ri, fip, is_failure):
ri._add_fip_addr_to_device = mock.Mock(return_value=is_failure)

View File

@ -198,7 +198,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
self.ctx,
port['id'])
def test_prevent__delete_floatingip_agent_gateway_port(self):
def test_prevent_delete_floatingip_agent_gateway_port(self):
port = {
'id': 'my_port_id',
'fixed_ips': mock.ANY,
@ -253,7 +253,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
'_check_fips_availability_on_host_ext_net') as cfips,\
mock.patch.object(
self.mixin,
'_delete_floatingip_agent_gateway_port') as dfips:
'delete_floatingip_agent_gateway_port') as dfips:
gfips.return_value = floatingip
gvm.return_value = 'my-host'
cfips.return_value = True
@ -305,7 +305,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
plugin = mock.Mock()
gp.return_value = plugin
plugin.get_ports.return_value = ports
self.mixin._delete_floatingip_agent_gateway_port(
self.mixin.delete_floatingip_agent_gateway_port(
self.ctx, port_host, 'ext_network_id')
plugin.get_ports.assert_called_with(self.ctx, filters={
'network_id': ['ext_network_id'],
@ -317,10 +317,10 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
plugin.ipam.delete_port.assert_called_with(
self.ctx, 'my_new_port_id')
def test__delete_floatingip_agent_gateway_port_without_host_id(self):
def test_delete_floatingip_agent_gateway_port_without_host_id(self):
self._helper_delete_floatingip_agent_gateway_port(None)
def test__delete_floatingip_agent_gateway_port_with_host_id(self):
def test_delete_floatingip_agent_gateway_port_with_host_id(self):
self._helper_delete_floatingip_agent_gateway_port(
'foo_host')
@ -346,7 +346,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
'delete_csnat_router_interface_ports') as del_csnat_port,\
mock.patch.object(
self.mixin,
'_delete_floatingip_agent_gateway_port') as del_agent_gw_port:
'delete_floatingip_agent_gateway_port') as del_agent_gw_port:
plugin = mock.Mock()
gp.return_value = plugin
plugin.get_ports.return_value = port