diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index c39e52ad95c..c21edd69384 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -580,8 +580,10 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, ri, ex_gw_port) fip_statuses = self._configure_fip_addresses(ri, interface_name) - except (n_exc.FloatingIpSetupException, n_exc.IpTablesApplyException): + except (n_exc.FloatingIpSetupException, + n_exc.IpTablesApplyException) as e: # All floating IPs must be put in error state + LOG.exception(e) fip_statuses = self._put_fips_in_error_state(ri) self._update_fip_statuses(ri, existing_floating_ips, fip_statuses) @@ -665,17 +667,18 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, def create_dvr_fip_interfaces(self, ri, ex_gw_port): floating_ips = self.get_floating_ips(ri) + fip_agent_port = self.get_floating_agent_gw_interface( + ri, ex_gw_port['network_id']) + LOG.debug("FloatingIP agent gateway port received from the plugin: " + "%s", fip_agent_port) if floating_ips: is_first = ri.fip_ns.subscribe(ri.router_id) - if is_first: - agent_gateway_port = ( - self.plugin_rpc.get_agent_gateway_port( - self.context, ex_gw_port['network_id'])) - if 'subnet' not in agent_gateway_port: + if is_first and fip_agent_port: + if 'subnet' not in fip_agent_port: LOG.error(_LE('Missing subnet/agent_gateway_port')) else: - self._set_subnet_info(agent_gateway_port) - ri.fip_ns.create_gateway_port(agent_gateway_port) + self._set_subnet_info(fip_agent_port) + ri.fip_ns.create_gateway_port(fip_agent_port) if ri.fip_ns.agent_gateway_port and floating_ips: if ri.dist_fip_count == 0: @@ -802,6 +805,12 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, floating_ips = [i for i in floating_ips if i['host'] == self.host] return floating_ips + def get_floating_agent_gw_interface(self, ri, ext_net_id): + """Filter Floating Agent GW port for the external network.""" + fip_ports = ri.router.get(l3_constants.FLOATINGIP_AGENT_INTF_KEY, []) + return next( + (p for p in fip_ports if p['network_id'] == ext_net_id), None) + def external_gateway_added(self, ri, ex_gw_port, interface_name): if ri.router['distributed']: ip_wrapr = ip_lib.IPWrapper(self.root_helper, namespace=ri.ns_name) diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index af3961904aa..a018d320195 100755 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -638,12 +638,42 @@ class TestDvrRouter(L3AgentTestFramework): floating_ip['port_id'] = internal_ports[0]['id'] floating_ip['status'] = 'ACTIVE' - if not enable_snat: - return router - self._add_snat_port_info_to_router(router, internal_ports) + # FIP has a dependency on external gateway. So we need to create + # the snat_port info and fip_agent_gw_port_info irrespective of + # the agent type the dvr supports. The namespace creation is + # dependent on the agent_type. + external_gw_port = router['gw_port'] + self._add_fip_agent_gw_port_info_to_router(router, external_gw_port) return router + def _add_fip_agent_gw_port_info_to_router(self, router, external_gw_port): + # Add fip agent gateway port information to the router_info + fip_gw_port_list = router.get( + l3_constants.FLOATINGIP_AGENT_INTF_KEY, []) + if not fip_gw_port_list and external_gw_port: + # Get values from external gateway port + fixed_ip = external_gw_port['fixed_ips'][0] + float_subnet = external_gw_port['subnet'] + port_ip = fixed_ip['ip_address'] + # Pick an ip address which is not the same as port_ip + fip_gw_port_ip = str(netaddr.IPAddress(port_ip) + 5) + # Add floatingip agent gateway port info to router + router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = [ + {'subnet': + {'cidr': float_subnet['cidr'], + 'gateway_ip': float_subnet['gateway_ip'], + 'id': fixed_ip['subnet_id']}, + 'network_id': external_gw_port['network_id'], + 'device_owner': 'network:floatingip_agent_gateway', + 'mac_address': 'fa:16:3e:80:8d:89', + 'binding:host_id': self.agent.conf.host, + 'fixed_ips': [{'subnet_id': fixed_ip['subnet_id'], + 'ip_address': fip_gw_port_ip}], + 'id': _uuid(), + 'device_id': _uuid()} + ] + def _add_snat_port_info_to_router(self, router, internal_ports): # Add snat port information to the router snat_port_list = router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) @@ -731,14 +761,19 @@ class TestDvrRouter(L3AgentTestFramework): # is created with the ip address of the external gateway port floating_ips = router.router[l3_constants.FLOATINGIP_KEY] self.assertTrue(floating_ips) + # We need to fetch the floatingip agent gateway port info + # from the router_info + floating_agent_gw_port = ( + router.router[l3_constants.FLOATINGIP_AGENT_INTF_KEY]) + self.assertTrue(floating_agent_gw_port) - external_port = self.agent._get_ex_gw_port(router) + external_gw_port = floating_agent_gw_port[0] fip_ns = self.agent.get_fip_ns(floating_ips[0]['floating_network_id']) fip_ns_name = fip_ns.get_name() fg_port_created_succesfully = ip_lib.device_exists_with_ip_mac( - fip_ns.get_ext_device_name(external_port['id']), - external_port['ip_cidr'], - external_port['mac_address'], + fip_ns.get_ext_device_name(external_gw_port['id']), + external_gw_port['ip_cidr'], + external_gw_port['mac_address'], fip_ns_name, self.root_helper) self.assertTrue(fg_port_created_succesfully) # Check fpr-router device has been created diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index 7b486e66fb0..5cd7f31db88 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -883,6 +883,77 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): for chain, rule in rules: nat.add_rule.assert_any_call(chain, rule, tag='floating_ip') + def test_get_floating_agent_gw_interfaces(self): + fake_network_id = _uuid() + agent_gateway_port = ( + [{'fixed_ips': [{'ip_address': '20.0.0.30', + 'subnet_id': _uuid()}], + 'subnet': {'gateway_ip': '20.0.0.1'}, + 'id': _uuid(), + 'binding:host_id': 'myhost', + 'device_owner': 'network:floatingip_agent_gateway', + 'network_id': fake_network_id, + 'mac_address': 'ca:fe:de:ad:be:ef', + 'ip_cidr': '20.0.0.30/24'}] + ) + + router = prepare_router_data(enable_snat=True) + router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port + router['distributed'] = True + ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs) + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + self.assertEqual( + agent_gateway_port[0], + agent.get_floating_agent_gw_interface(ri, fake_network_id)) + + def test_create_dvr_fip_interfaces(self): + fake_network_id = _uuid() + fake_floatingips = {'floatingips': [ + {'id': _uuid(), + 'floating_ip_address': '20.0.0.3', + 'fixed_ip_address': '192.168.0.1', + 'floating_network_id': _uuid(), + 'port_id': _uuid(), + 'host': HOSTNAME}]} + agent_gateway_port = ( + [{'fixed_ips': [{'ip_address': '20.0.0.30', + 'subnet_id': _uuid()}], + 'subnet': {'gateway_ip': '20.0.0.1'}, + 'id': _uuid(), + 'network_id': fake_network_id, + 'mac_address': 'ca:fe:de:ad:be:ef', + 'ip_cidr': '20.0.0.30/24'}] + ) + + router = prepare_router_data(enable_snat=True) + router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] + router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port + router['distributed'] = True + ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs) + + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ext_gw_port = ri.router.get('gw_port') + ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) + ri.dist_fip_count = 0 + ri.fip_ns.subscribe = mock.Mock() + + with contextlib.nested(mock.patch.object(agent, + 'get_floating_ips'), + mock.patch.object( + agent, 'get_floating_agent_gw_interface'), + mock.patch.object( + agent, '_set_subnet_info') + ) as (fips, + fip_gw_port, + sub_info): + fips.return_value = fake_floatingips + fip_gw_port.return_value = agent_gateway_port[0] + agent.create_dvr_fip_interfaces(ri, ext_gw_port) + self.assertTrue(fip_gw_port.called) + self.assertTrue(fips.called) + self.assertEqual(ri.fip_ns.agent_gateway_port, + agent_gateway_port[0]) + def test_process_router_cent_floating_ip_add(self): fake_floatingips = {'floatingips': [ {'id': _uuid(),