From 9e645b145a667a376cb5d5731044321dc0d6122c Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Tue, 20 Jan 2015 16:48:47 +0000 Subject: [PATCH] Move DVR floating ip methods to dvr_router Change-Id: Ic751d87df2d2fb44683f1dd555e8ed86dff3aeaf Partially-Implements: bp/restructure-l3-agent --- neutron/agent/l3/agent.py | 3 +- neutron/agent/l3/dvr.py | 63 +------------- neutron/agent/l3/dvr_router.py | 67 +++++++++++++++ neutron/tests/unit/test_dvr_router.py | 114 ++++++++++++++++++++++++++ neutron/tests/unit/test_l3_agent.py | 72 ---------------- 5 files changed, 187 insertions(+), 132 deletions(-) create mode 100644 neutron/tests/unit/test_dvr_router.py diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 5d5ef5987..dc40135e4 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -714,7 +714,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, if ri.router['distributed']: # Special Handling for DVR - update FIP namespace # and ri.namespace to handle DVR based FIP - self.floating_ip_added_dist(ri, fip, ip_cidr) + ri.floating_ip_added_dist(fip, ip_cidr) else: # As GARP is processed in a distinct thread the call below # won't raise an exception to be handled. @@ -735,6 +735,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, namespace=ri.ns_name, ip=ip_cidr) if ri.router['distributed']: + #TODO(Carl) Call this method on ri. Needs namespace work. self.floating_ip_removed_dist(ri, ip_cidr) def _get_router_cidrs(self, ri, device): diff --git a/neutron/agent/l3/dvr.py b/neutron/agent/l3/dvr.py index fceb3f573..0e4fa981a 100644 --- a/neutron/agent/l3/dvr.py +++ b/neutron/agent/l3/dvr.py @@ -168,67 +168,12 @@ class AgentMixin(object): # kicks the FW Agent to add rules for the snat namespace self.process_router_add(ri) - def floating_ip_added_dist(self, ri, fip, fip_cidr): - """Add floating IP to FIP namespace.""" - floating_ip = fip['floating_ip_address'] - fixed_ip = fip['fixed_ip_address'] - rule_pr = ri.fip_ns.allocate_rule_priority() - ri.floating_ips_dict[floating_ip] = rule_pr - fip_2_rtr_name = ri.fip_ns.get_int_device_name(ri.router_id) - ip_rule = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - ip_rule.add(fixed_ip, dvr_fip_ns.FIP_RT_TBL, rule_pr) - #Add routing rule in fip namespace - fip_ns_name = ri.fip_ns.get_name() - rtr_2_fip, _ = ri.rtr_fip_subnet.get_pair() - device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, - namespace=fip_ns_name) - device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) - interface_name = ( - ri.fip_ns.get_ext_device_name(ri.fip_ns.agent_gateway_port['id'])) - ip_lib.send_garp_for_proxyarp(fip_ns_name, - interface_name, - floating_ip, - self.conf.send_arp_for_ha, - self.root_helper) - # update internal structures - ri.dist_fip_count = ri.dist_fip_count + 1 - def floating_ip_removed_dist(self, ri, fip_cidr): """Remove floating IP from FIP namespace.""" - floating_ip = fip_cidr.split('/')[0] - rtr_2_fip_name = ri.fip_ns.get_rtr_ext_device_name(ri.router_id) - fip_2_rtr_name = ri.fip_ns.get_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 = ri.fip_ns.get_name() - if floating_ip in ri.floating_ips_dict: - rule_pr = ri.floating_ips_dict[floating_ip] - ip_rule = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - ip_rule.delete(floating_ip, dvr_fip_ns.FIP_RT_TBL, rule_pr) - ri.fip_ns.deallocate_rule_priority(rule_pr) - #TODO(rajeev): Handle else case - exception/log? - - device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, - namespace=fip_ns_name) - - device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) - # check if this is the last FIP for this router - ri.dist_fip_count = ri.dist_fip_count - 1 - if ri.dist_fip_count == 0: - #remove default route entry - device = ip_lib.IPDevice(rtr_2_fip_name, self.root_helper, - namespace=ri.ns_name) - ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=fip_ns_name) - device.route.delete_gateway(str(fip_2_rtr.ip), - table=dvr_fip_ns.FIP_RT_TBL) - ri.fip_ns.local_subnets.release(ri.router_id) - ri.rtr_fip_subnet = None - ns_ip.del_veth(fip_2_rtr_name) - is_last = ri.fip_ns.unsubscribe(ri.router_id) - # clean up fip-namespace if this is the last FIP - if is_last: - self._destroy_fip_namespace(fip_ns_name) + is_last = ri.floating_ip_removed_dist(fip_cidr) + # clean up fip-namespace if this is the last FIP + if is_last: + self._destroy_fip_namespace(ri.fip_ns.get_name()) def _snat_redirect_add(self, ri, gateway, sn_port, sn_int): """Adds rules and routes for SNAT redirection.""" diff --git a/neutron/agent/l3/dvr_router.py b/neutron/agent/l3/dvr_router.py index 8ad32da9a..7b44e7df8 100644 --- a/neutron/agent/l3/dvr_router.py +++ b/neutron/agent/l3/dvr_router.py @@ -12,7 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import router_info as router +from neutron.agent.linux import ip_lib class DvrRouter(router.RouterInfo): @@ -47,3 +49,68 @@ class DvrRouter(router.RouterInfo): {'interface_name': interface_name}) self.iptables_manager.ipv4['nat'].add_rule(*rule) self.iptables_manager.apply() + + def floating_ip_added_dist(self, fip, fip_cidr): + """Add floating IP to FIP namespace.""" + floating_ip = fip['floating_ip_address'] + fixed_ip = fip['fixed_ip_address'] + rule_pr = self.fip_ns.allocate_rule_priority() + self.floating_ips_dict[floating_ip] = rule_pr + fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) + ip_rule = ip_lib.IpRule(self.root_helper, namespace=self.ns_name) + ip_rule.add(fixed_ip, dvr_fip_ns.FIP_RT_TBL, rule_pr) + #Add routing rule in fip namespace + fip_ns_name = self.fip_ns.get_name() + rtr_2_fip, _ = self.rtr_fip_subnet.get_pair() + device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, + namespace=fip_ns_name) + device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) + interface_name = ( + self.fip_ns.get_ext_device_name( + self.fip_ns.agent_gateway_port['id'])) + ip_lib.send_garp_for_proxyarp(fip_ns_name, + interface_name, + floating_ip, + self.agent_conf.send_arp_for_ha, + self.root_helper) + # update internal structures + self.dist_fip_count = self.dist_fip_count + 1 + + def floating_ip_removed_dist(self, fip_cidr): + """Remove floating IP from FIP namespace.""" + floating_ip = fip_cidr.split('/')[0] + rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) + fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) + if self.rtr_fip_subnet is None: + self.rtr_fip_subnet = self.local_subnets.allocate(self.router_id) + + rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() + fip_ns_name = self.fip_ns.get_name() + if floating_ip in self.floating_ips_dict: + rule_pr = self.floating_ips_dict[floating_ip] + ip_rule = ip_lib.IpRule(self.root_helper, namespace=self.ns_name) + ip_rule.delete(floating_ip, dvr_fip_ns.FIP_RT_TBL, rule_pr) + self.fip_ns.deallocate_rule_priority(rule_pr) + #TODO(rajeev): Handle else case - exception/log? + + device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, + namespace=fip_ns_name) + + device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) + # check if this is the last FIP for this router + self.dist_fip_count = self.dist_fip_count - 1 + is_last = False + if self.dist_fip_count == 0: + #remove default route entry + device = ip_lib.IPDevice(rtr_2_fip_name, + self.root_helper, + namespace=self.ns_name) + ns_ip = ip_lib.IPWrapper(self.root_helper, + namespace=fip_ns_name) + device.route.delete_gateway(str(fip_2_rtr.ip), + table=dvr_fip_ns.FIP_RT_TBL) + self.fip_ns.local_subnets.release(self.router_id) + self.rtr_fip_subnet = None + ns_ip.del_veth(fip_2_rtr_name) + is_last = self.fip_ns.unsubscribe(self.router_id) + return is_last diff --git a/neutron/tests/unit/test_dvr_router.py b/neutron/tests/unit/test_dvr_router.py new file mode 100644 index 000000000..4552d73d7 --- /dev/null +++ b/neutron/tests/unit/test_dvr_router.py @@ -0,0 +1,114 @@ +# Copyright (c) 2015 Openstack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import mock +import netaddr + +from neutron.agent.l3 import dvr_router +from neutron.agent.l3 import link_local_allocator as lla +from neutron.agent.linux import ip_lib +from neutron.common import utils as common_utils +from neutron.openstack.common import uuidutils +from neutron.tests import base + +_uuid = uuidutils.generate_uuid +FIP_PRI = 32768 +HOSTNAME = 'myhost' + + +class TestDvrRouterOperations(base.BaseTestCase): + def setUp(self): + super(TestDvrRouterOperations, self).setUp() + + def _create_router(self, router, **kwargs): + agent_conf = mock.Mock() + return dvr_router.DvrRouter(mock.sentinel.router_id, + router, + mock.sentinel.root_helper, + agent_conf, + mock.sentinel.interface_driver, + **kwargs) + + @mock.patch.object(ip_lib, 'send_garp_for_proxyarp') + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'IpRule') + def test_floating_ip_added_dist(self, mIpRule, mIPDevice, mock_arp): + router = mock.MagicMock() + ri = self._create_router(router) + ext_net_id = _uuid() + agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', + 'subnet_id': _uuid()}], + 'subnet': {'gateway_ip': '20.0.0.1'}, + 'id': _uuid(), + 'network_id': ext_net_id, + 'mac_address': 'ca:fe:de:ad:be:ef', + 'ip_cidr': '20.0.0.30/24'} + + fip = {'id': _uuid(), + 'host': HOSTNAME, + 'floating_ip_address': '15.1.2.3', + 'fixed_ip_address': '192.168.0.1', + 'floating_network_id': ext_net_id, + 'port_id': _uuid()} + ri.fip_ns = mock.Mock() + ri.fip_ns.agent_gateway_port = agent_gw_port + ri.fip_ns.allocate_rule_priority.return_value = FIP_PRI + 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']) + ri.floating_ip_added_dist(fip, ip_cidr) + mIpRule().add.assert_called_with('192.168.0.1', 16, FIP_PRI) + self.assertEqual(1, ri.dist_fip_count) + # TODO(mrsmith): add more asserts + + @mock.patch.object(ip_lib, 'IPWrapper') + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'IpRule') + def test_floating_ip_removed_dist(self, mIpRule, mIPDevice, mIPWrapper): + router = mock.MagicMock() + ri = self._create_router(router) + + agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', + 'subnet_id': _uuid()}], + 'subnet': {'gateway_ip': '20.0.0.1'}, + 'id': _uuid(), + 'network_id': _uuid(), + 'mac_address': 'ca:fe:de:ad:be:ef', + 'ip_cidr': '20.0.0.30/24'} + fip_cidr = '11.22.33.44/24' + + ri.dist_fip_count = 2 + ri.fip_ns = mock.Mock() + ri.fip_ns.get_name.return_value = 'fip_ns_name' + ri.floating_ips_dict['11.22.33.44'] = FIP_PRI + ri.fip_2_rtr = '11.22.33.42' + ri.rtr_2_fip = '11.22.33.40' + ri.fip_ns.agent_gateway_port = agent_gw_port + s = lla.LinkLocalAddressPair('169.254.30.42/31') + ri.rtr_fip_subnet = s + ri.floating_ip_removed_dist(fip_cidr) + mIpRule().delete.assert_called_with( + str(netaddr.IPNetwork(fip_cidr).ip), 16, FIP_PRI) + mIPDevice().route.delete_route.assert_called_with(fip_cidr, str(s.ip)) + self.assertFalse(ri.fip_ns.unsubscribe.called) + + ri.dist_fip_count = 1 + ri.rtr_fip_subnet = lla.LinkLocalAddressPair('15.1.2.3/32') + _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() + ri.floating_ip_removed_dist(fip_cidr) + mIPWrapper().del_veth.assert_called_once_with( + ri.fip_ns.get_int_device_name(router['id'])) + mIPDevice().route.delete_gateway.assert_called_once_with( + str(fip_to_rtr.ip), table=16) + ri.fip_ns.unsubscribe.assert_called_once_with(ri.router_id) diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index 7206e4b3b..b066a1761 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -38,7 +38,6 @@ from neutron.agent.metadata import driver as metadata_driver from neutron.common import config as base_config from neutron.common import constants as l3_constants from neutron.common import exceptions as n_exc -from neutron.common import utils as common_utils from neutron.i18n import _LE from neutron.openstack.common import log from neutron.openstack.common import uuidutils @@ -1772,77 +1771,6 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.assertEqual(self.mock_driver.plug.call_count, 3) self.assertEqual(self.mock_driver.init_l3.call_count, 3) - def test_floating_ip_added_dist(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() - ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs) - ext_net_id = _uuid() - agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', - 'subnet_id': _uuid()}], - 'subnet': {'gateway_ip': '20.0.0.1'}, - 'id': _uuid(), - 'network_id': ext_net_id, - 'mac_address': 'ca:fe:de:ad:be:ef', - 'ip_cidr': '20.0.0.30/24'} - - fip = {'id': _uuid(), - 'host': HOSTNAME, - 'floating_ip_address': '15.1.2.3', - 'fixed_ip_address': '192.168.0.1', - 'floating_network_id': ext_net_id, - 'port_id': _uuid()} - ri.fip_ns = agent.get_fip_ns(ext_net_id) - ri.fip_ns.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.assert_called_with('192.168.0.1', 16, FIP_PRI) - # TODO(mrsmith): add more asserts - - @mock.patch.object(lla.LinkLocalAllocator, '_write') - def test_floating_ip_removed_dist(self, write): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() - agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', - 'subnet_id': _uuid()}], - 'subnet': {'gateway_ip': '20.0.0.1'}, - 'id': _uuid(), - 'network_id': _uuid(), - 'mac_address': 'ca:fe:de:ad:be:ef', - 'ip_cidr': '20.0.0.30/24'} - fip_cidr = '11.22.33.44/24' - - ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs) - ri.dist_fip_count = 2 - ri.fip_ns = agent.get_fip_ns(agent._fetch_external_net_id()) - ri.fip_ns.unsubscribe = mock.Mock() - ri.floating_ips_dict['11.22.33.44'] = FIP_PRI - ri.fip_2_rtr = '11.22.33.42' - ri.rtr_2_fip = '11.22.33.40' - agent.agent_gateway_port = agent_gw_port - s = lla.LinkLocalAddressPair('169.254.30.42/31') - ri.rtr_fip_subnet = s - agent.floating_ip_removed_dist(ri, fip_cidr) - floating_ip = fip_cidr.split('/')[0] - self.mock_rule.delete.assert_called_with(floating_ip, 16, FIP_PRI) - self.mock_ip_dev.route.delete_route.assert_called_with(fip_cidr, - str(s.ip)) - self.assertFalse(ri.fip_ns.unsubscribe.called) - - with mock.patch.object(agent, '_destroy_fip_namespace') as f: - ri.dist_fip_count = 1 - fip_ns_name = ri.fip_ns.get_name() - ri.rtr_fip_subnet = ri.fip_ns.local_subnets.allocate(ri.router_id) - _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() - agent.floating_ip_removed_dist(ri, fip_cidr) - self.mock_ip.del_veth.assert_called_once_with( - ri.fip_ns.get_int_device_name(router['id'])) - self.mock_ip_dev.route.delete_gateway.assert_called_once_with( - str(fip_to_rtr.ip), table=16) - f.assert_called_once_with(fip_ns_name) - ri.fip_ns.unsubscribe.assert_called_once_with(ri.router_id) - def test_get_service_plugin_list(self): service_plugins = [p_const.L3_ROUTER_NAT] self.plugin_api.get_service_plugin_list.return_value = service_plugins -- 2.45.2