diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 847ab32ad1e..102e57edf5a 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -580,6 +580,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, # TODO(mrsmith) - we shouldn't need to check here if 'distributed' not in ri.router: ri.router['distributed'] = False + self.scan_fip_ports(ri) self._process_internal_ports(ri) self._process_external(ri) # Process static routes for router @@ -656,14 +657,19 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self._create_agent_gateway_port(ri, floating_ips[0] ['floating_network_id']) - if self.agent_gateway_port: - if floating_ips and ri.dist_fip_count == 0: - self.create_rtr_2_fip_link(ri, floating_ips[0] - ['floating_network_id']) + if self.agent_gateway_port and floating_ips: + fip_net_id = floating_ips[0]['floating_network_id'] + self.create_rtr_2_fip_link(ri, fip_net_id) def _get_external_device_interface_name(self, ri, ex_gw_port): if ri.router['distributed']: - if self.agent_gateway_port: + fip_int = self.get_fip_int_device_name(ri.router_id) + # TODO(mrsmith) refactor for multiple ext nets + fip_ns = self.get_fip_ns_name(str(self._fetch_external_net_id())) + + if ip_lib.device_exists(fip_int, + root_helper=self.root_helper, + namespace=fip_ns): return self.get_rtr_int_device_name(ri.router_id) else: return self.get_external_device_name(ex_gw_port['id']) diff --git a/neutron/agent/l3/dvr.py b/neutron/agent/l3/dvr.py index cb72666e9d1..e64a4007978 100644 --- a/neutron/agent/l3/dvr.py +++ b/neutron/agent/l3/dvr.py @@ -20,6 +20,7 @@ from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.linux import ip_lib from neutron.agent.linux import iptables_manager from neutron.common import constants as l3_constants +from neutron.common import utils as common_utils from neutron.i18n import _LE from neutron.openstack.common import log as logging @@ -122,6 +123,24 @@ class AgentMixin(object): if f['subnet_id'] == subnet_id: return port + def scan_fip_ports(self, ri): + # don't scan if not dvr or count is not None + if not ri.router.get('distributed') or ri.dist_fip_count is not None: + return + + # scan system for any existing fip ports + ri.dist_fip_count = 0 + rtr_2_fip_interface = self.get_rtr_int_device_name(ri.router_id) + if ip_lib.device_exists(rtr_2_fip_interface, + root_helper=self.root_helper, + namespace=ri.ns_name): + device = ip_lib.IPDevice(rtr_2_fip_interface, self.root_helper, + namespace=ri.ns_name) + existing_cidrs = [addr['cidr'] for addr in device.addr.list()] + fip_cidrs = [c for c in existing_cidrs if + common_utils.is_cidr_host(c)] + ri.dist_fip_count = len(fip_cidrs) + def get_fip_ext_device_name(self, port_id): return (FIP_EXT_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] @@ -361,17 +380,17 @@ class AgentMixin(object): floating_ip = fip_cidr.split('/')[0] rtr_2_fip_name = self.get_rtr_int_device_name(ri.router_id) fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) + if ri.rtr_fip_subnet is None: + ri.rtr_fip_subnet = self.local_subnets.allocate(ri.router_id) rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() fip_ns_name = self.get_fip_ns_name(str(self._fetch_external_net_id())) ip_rule_rtr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) if floating_ip in ri.floating_ips_dict: rule_pr = ri.floating_ips_dict[floating_ip] + ip_rule_rtr.delete_rule_priority(rule_pr) + self.fip_priorities.add(rule_pr) #TODO(rajeev): Handle else case - exception/log? - else: - rule_pr = None - ip_rule_rtr.delete_rule_priority(rule_pr) - self.fip_priorities.add(rule_pr) device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, namespace=fip_ns_name) diff --git a/neutron/agent/l3/dvr_router.py b/neutron/agent/l3/dvr_router.py index b948fcddf23..6822a15aaf7 100644 --- a/neutron/agent/l3/dvr_router.py +++ b/neutron/agent/l3/dvr_router.py @@ -23,4 +23,4 @@ class DvrRouter(router.RouterInfo): self.snat_iptables_manager = None # Linklocal subnet for router and floating IP namespace link self.rtr_fip_subnet = None - self.dist_fip_count = 0 + self.dist_fip_count = None diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index 52b85e7f64d..7a1a067e803 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -819,6 +819,42 @@ class TestBasicRouterOperations(base.BaseTestCase): 4, '1.5.25.15', '00:44:33:22:11:55') agent.router_deleted(None, router['id']) + @mock.patch('neutron.agent.linux.ip_lib.IPDevice') + def _test_scan_fip_ports(self, ri, ip_list, IPDevice): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + self.device_exists.return_value = True + IPDevice.return_value = device = mock.Mock() + device.addr.list.return_value = ip_list + agent.scan_fip_ports(ri) + + def test_scan_fip_ports_restart_fips(self): + router = prepare_router_data() + ri = dvr_router.DvrRouter(router['id'], self.conf.root_helper, + router=router) + ri.router['distributed'] = True + ip_list = [{'cidr': '111.2.3.4/32'}, {'cidr': '111.2.3.5/32'}] + self._test_scan_fip_ports(ri, ip_list) + self.assertEqual(ri.dist_fip_count, 2) + + def test_scan_fip_ports_restart_none(self): + router = prepare_router_data() + ri = dvr_router.DvrRouter(router['id'], self.conf.root_helper, + router=router) + ri.router['distributed'] = True + ip_list = [] + self._test_scan_fip_ports(ri, ip_list) + self.assertEqual(ri.dist_fip_count, 0) + + def test_scan_fip_ports_restart_zero(self): + router = prepare_router_data() + ri = dvr_router.DvrRouter(router['id'], self.conf.root_helper, + router=router) + ri.router['distributed'] = True + ri.dist_fip_count = 0 + ip_list = None + self._test_scan_fip_ports(ri, ip_list) + self.assertEqual(ri.dist_fip_count, 0) + def test_process_cent_router(self): router = prepare_router_data() ri = l3router.RouterInfo(router['id'], self.conf.root_helper, @@ -827,8 +863,8 @@ class TestBasicRouterOperations(base.BaseTestCase): def test_process_dist_router(self): router = prepare_router_data() - ri = l3router.RouterInfo(router['id'], self.conf.root_helper, - router=router) + ri = dvr_router.DvrRouter(router['id'], self.conf.root_helper, + router=router) subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0]) ri.router['distributed'] = True ri.router['_snat_router_interfaces'] = [{ @@ -994,6 +1030,7 @@ class TestBasicRouterOperations(base.BaseTestCase): ri = dvr_router.DvrRouter(router['id'], self.conf.root_helper, router=router) ri.iptables_manager.ipv4['nat'] = mock.MagicMock() + ri.dist_fip_count = 0 agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.host = HOSTNAME agent.agent_gateway_port = ( @@ -1970,6 +2007,7 @@ class TestBasicRouterOperations(base.BaseTestCase): 'port_id': _uuid()} agent.agent_gateway_port = agent_gw_port ri.rtr_fip_subnet = lla.LinkLocalAddressPair('169.254.30.42/31') + ri.dist_fip_count = 0 ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) agent.floating_ip_added_dist(ri, fip, ip_cidr) self.mock_rule.add_rule_from.assert_called_with('192.168.0.1',