Merge "Refactor remove_router_interface() for DVR"

This commit is contained in:
Jenkins 2016-01-27 03:20:57 +00:00 committed by Gerrit Code Review
commit 073ac9afc4
4 changed files with 85 additions and 69 deletions

View File

@ -643,11 +643,12 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
@staticmethod
def _make_router_interface_info(
router_id, tenant_id, port_id, subnet_id, subnet_ids):
router_id, tenant_id, port_id, network_id, subnet_id, subnet_ids):
return {
'id': router_id,
'tenant_id': tenant_id,
'port_id': port_id,
'network_id': network_id,
'subnet_id': subnet_id, # deprecated by IPv6 multi-prefix
'subnet_ids': subnet_ids
}
@ -695,8 +696,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
interface_info=interface_info)
return self._make_router_interface_info(
router.id, port['tenant_id'], port['id'], subnets[-1]['id'],
[subnet['id'] for subnet in subnets])
router.id, port['tenant_id'], port['id'], port['network_id'],
subnets[-1]['id'], [subnet['id'] for subnet in subnets])
def _confirm_router_interface_not_in_use(self, context, router_id,
subnet_id):
@ -809,7 +810,8 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase):
cidrs=[x['cidr'] for x in subnets],
network_id=gw_network_id)
return self._make_router_interface_info(router_id, port['tenant_id'],
port['id'], subnets[0]['id'],
port['id'], port['network_id'],
subnets[0]['id'],
[subnet['id'] for subnet in
subnets])

View File

@ -298,8 +298,8 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
LOG.debug("CSNAT port updated for IPv6 subnet: "
"%s", updated_port)
router_interface_info = self._make_router_interface_info(
router_id, port['tenant_id'], port['id'], subnet['id'],
[subnet['id']])
router_id, port['tenant_id'], port['id'], port['network_id'],
subnet['id'], [subnet['id']])
self.notify_router_interface_action(
context, router_interface_info, 'add')
return router_interface_info
@ -322,7 +322,7 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
return port
def _check_for_multiprefix_csnat_port_and_update(
self, context, router, subnet, port):
self, context, router, network_id, subnet_id):
"""Checks if the csnat port contains multiple ipv6 prefixes.
If the csnat port contains multiple ipv6 prefixes for the given
@ -331,13 +331,11 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
it with the right fixed_ip.
This function returns true if it is a multiprefix port.
"""
subnet_id = subnet['id']
if router.gw_port:
# If router has a gateway port, check if it has IPV6 subnet
cs_port = (
self._find_router_port_by_network_and_device_owner(
router, subnet['network_id'],
l3_const.DEVICE_OWNER_ROUTER_SNAT))
router, network_id, l3_const.DEVICE_OWNER_ROUTER_SNAT))
if cs_port:
fixed_ips = (
[fixedip for fixedip in
@ -351,68 +349,49 @@ class L3_NAT_with_dvr_db_mixin(l3_db.L3_NAT_db_mixin,
return True
return False
def _check_dvr_router_remove_required_and_notify_agent(
self, context, router, port, subnet):
if router.extra_attributes.distributed:
is_multiple_prefix_csport = (
self._check_for_multiprefix_csnat_port_and_update(
context, router, subnet, port))
if not is_multiple_prefix_csport:
# Single prefix port - go ahead and delete the port
self.delete_csnat_router_interface_ports(
context.elevated(), router, subnet_id=subnet['id'])
plugin = manager.NeutronManager.get_service_plugins().get(
constants.L3_ROUTER_NAT)
l3_agents = plugin.get_l3_agents_hosting_routers(context,
[router['id']])
subnet_ids = plugin.get_subnet_ids_on_router(
context, router['id'])
if subnet_ids:
binding_table = l3_dvrsched_db.CentralizedSnatL3AgentBinding
snat_binding = context.session.query(binding_table).filter_by(
router_id=router['id']).first()
for l3_agent in l3_agents:
is_this_snat_agent = (
snat_binding and
snat_binding.l3_agent_id == l3_agent['id'])
if (is_this_snat_agent or
plugin.check_dvr_serviceable_ports_on_host(
context, l3_agent['host'], subnet_ids)):
continue
plugin.remove_router_from_l3_agent(
context, l3_agent['id'], router['id'])
router_interface_info = self._make_router_interface_info(
router['id'], port['tenant_id'], port['id'], subnet['id'],
[subnet['id']])
self.notify_router_interface_action(
context, router_interface_info, 'remove')
return router_interface_info
def remove_router_interface(self, context, router_id, interface_info):
remove_by_port, remove_by_subnet = (
self._validate_interface_info(interface_info, for_removal=True)
)
port_id = interface_info.get('port_id')
subnet_id = interface_info.get('subnet_id')
router = self._get_router(context, router_id)
device_owner = self._get_device_owner(context, router)
if not router.extra_attributes.distributed:
return super(
L3_NAT_with_dvr_db_mixin, self).remove_router_interface(
context, router_id, interface_info)
if remove_by_port:
port, subnets = self._remove_interface_by_port(
context, router_id, port_id, subnet_id, device_owner)
plugin = manager.NeutronManager.get_service_plugins().get(
constants.L3_ROUTER_NAT)
router_hosts_before = plugin._get_dvr_hosts_for_router(
context, router_id)
# remove_by_subnet is not used here, because the validation logic of
# _validate_interface_info ensures that at least one of remote_by_*
# is True.
else:
port, subnets = self._remove_interface_by_subnet(
context, router_id, subnet_id, device_owner)
interface_info = super(
L3_NAT_with_dvr_db_mixin, self).remove_router_interface(
context, router_id, interface_info)
subnet = subnets[0]
router_interface_info = (
self._check_dvr_router_remove_required_and_notify_agent(
context, router, port, subnet))
return router_interface_info
router_hosts_after = plugin._get_dvr_hosts_for_router(
context, router_id)
removed_hosts = set(router_hosts_before) - set(router_hosts_after)
if removed_hosts:
agents = plugin.get_l3_agents(context,
filters={'host': removed_hosts})
binding_table = l3_dvrsched_db.CentralizedSnatL3AgentBinding
snat_binding = context.session.query(binding_table).filter_by(
router_id=router_id).first()
for agent in agents:
is_this_snat_agent = (
snat_binding and snat_binding.l3_agent_id == agent['id'])
if not is_this_snat_agent:
plugin.remove_router_from_l3_agent(
context, agent['id'], router_id)
is_multiple_prefix_csport = (
self._check_for_multiprefix_csnat_port_and_update(
context, router, interface_info['network_id'],
interface_info['subnet_id']))
if not is_multiple_prefix_csport:
# Single prefix port - go ahead and delete the port
self.delete_csnat_router_interface_ports(
context.elevated(), router,
subnet_id=interface_info['subnet_id'])
return interface_info
def _get_snat_sync_interfaces(self, context, router_ids):
"""Query router interfaces that relate to list of router_ids."""

View File

@ -727,3 +727,40 @@ class L3DvrTestCase(ml2_test_base.ML2TestFramework):
self.assertEqual(self.l3_agent['id'],
agents['agents'][0]['id'])
self.assertFalse(remove_mock.called)
def test_remove_router_interface(self):
HOST1 = 'host1'
dvr_agent = helpers.register_l3_agent(
host=HOST1, agent_mode=constants.L3_AGENT_MODE_DVR)
router = self._create_router()
arg_list = (portbindings.HOST_ID,)
with self.subnet() as subnet,\
self.port(subnet=subnet,
device_owner=DEVICE_OWNER_COMPUTE,
arg_list=arg_list,
**{portbindings.HOST_ID: HOST1}):
l3_notifier = mock.Mock()
self.l3_plugin.l3_rpc_notifier = l3_notifier
self.l3_plugin.agent_notifiers[
constants.AGENT_TYPE_L3] = l3_notifier
self.l3_plugin.add_router_interface(
self.context, router['id'],
{'subnet_id': subnet['subnet']['id']})
self.l3_plugin.schedule_router(self.context, router['id'])
# router should be scheduled to the agent on HOST1
agents = self.l3_plugin.list_l3_agents_hosting_router(
self.context, router['id'])['agents']
self.assertEqual(1, len(agents))
self.assertEqual(dvr_agent['id'], agents[0]['id'])
self.l3_plugin.remove_router_interface(
self.context, router['id'],
{'subnet_id': subnet['subnet']['id']})
agents = self.l3_plugin.list_l3_agents_hosting_router(
self.context, router['id'])['agents']
self.assertEqual(0, len(agents))
l3_notifier.router_removed_from_agent.assert_called_once_with(
self.context, router['id'], HOST1)

View File

@ -471,7 +471,6 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
'distributed': True}
router = self._create_router(router_dict)
plugin = mock.MagicMock()
plugin.get_subnet_ids_on_router = mock.Mock()
with self.network() as net_ext,\
self.subnet() as subnet1,\
self.subnet(cidr='20.0.0.0/24') as subnet2:
@ -517,7 +516,6 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
dvr_ports = self.core_plugin.get_ports(
self.ctx, filters=dvr_filters)
self.assertEqual(1, len(dvr_ports))
self.assertEqual(1, plugin.get_subnet_ids_on_router.call_count)
def test__validate_router_migration_notify_advanced_services(self):
router = {'name': 'foo_router', 'admin_state_up': False}