]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Move Floating IP processing to Router classes
authorCarl Baldwin <carl.baldwin@hp.com>
Thu, 18 Dec 2014 17:48:19 +0000 (17:48 +0000)
committerCarl Baldwin <carl.baldwin@hp.com>
Fri, 20 Feb 2015 17:14:44 +0000 (17:14 +0000)
Change-Id: Id0822f1c3d586c813a09e4b442a21d3cb52943d0
Partially-Implements: bp/restructure-l3-agent

neutron/agent/l3/agent.py
neutron/agent/l3/dvr_router.py
neutron/agent/l3/ha_router.py
neutron/agent/l3/legacy_router.py
neutron/agent/l3/router_info.py
neutron/tests/functional/agent/test_l3_agent.py
neutron/tests/unit/agent/l3/test_dvr_router.py [moved from neutron/tests/unit/test_dvr_router.py with 68% similarity]
neutron/tests/unit/agent/l3/test_ha_router.py [new file with mode: 0644]
neutron/tests/unit/agent/l3/test_l3_router.py [new file with mode: 0644]
neutron/tests/unit/agent/l3/test_legacy_router.py [new file with mode: 0644]
neutron/tests/unit/test_l3_agent.py

index 88fd221b20bae9649f096372dae14ebdd3194171..659ea8fa9fb6a592b7cb62db27c086863530cc4e 100644 (file)
@@ -362,6 +362,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
         }
 
         if router.get('distributed'):
+            kwargs['host'] = self.host
             return dvr_router.DvrRouter(*args, **kwargs)
 
         if router.get('ha'):
@@ -471,7 +472,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
                                prefix=INTERNAL_DEV_PREFIX)
 
     def _process_external_gateway(self, ri):
-        ex_gw_port = self._get_ex_gw_port(ri)
+        ex_gw_port = ri.get_ex_gw_port()
         ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or
                          ri.ex_gw_port and ri.ex_gw_port['id'])
 
@@ -515,29 +516,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
             ri.perform_snat_action(self._handle_router_snat_rules,
                                    interface_name)
 
-    def _put_fips_in_error_state(self, ri):
-        fip_statuses = {}
-        for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []):
-            fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ERROR
-        return fip_statuses
-
-    def _process_snat_dnat_for_fip(self, ri):
-        try:
-            self.process_router_floating_ip_nat_rules(ri)
-        except Exception:
-            # TODO(salv-orlando): Less broad catching
-            raise n_exc.FloatingIpSetupException('L3 agent failure to setup '
-                'NAT for floating IPs')
-
-    def _configure_fip_addresses(self, ri, interface_name):
-        try:
-            return self.process_router_floating_ip_addresses(
-                ri, interface_name)
-        except Exception:
-            # TODO(salv-orlando): Less broad catching
-            raise n_exc.FloatingIpSetupException('L3 agent failure to setup '
-                'floating IPs')
-
     def _update_fip_statuses(self, ri, existing_floating_ips, fip_statuses):
         # Identify floating IPs which were disabled
         ri.floating_ips = set(fip_statuses.keys())
@@ -558,7 +536,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
         try:
             with ri.iptables_manager.defer_apply():
                 self._process_external_gateway(ri)
-                ex_gw_port = self._get_ex_gw_port(ri)
+                ex_gw_port = ri.get_ex_gw_port()
                 # TODO(Carl) Return after setting existing_floating_ips and
                 # still call _update_fip_statuses?
                 if not ex_gw_port:
@@ -568,19 +546,19 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
                 existing_floating_ips = ri.floating_ips
                 if ri.router['distributed']:
                     self.create_dvr_fip_interfaces(ri, ex_gw_port)
-                self._process_snat_dnat_for_fip(ri)
+                ri.process_snat_dnat_for_fip()
 
             # Once NAT rules for floating IPs are safely in place
             # configure their addresses on the external gateway port
             interface_name = self._get_external_device_interface_name(
                 ri, ex_gw_port)
-            fip_statuses = self._configure_fip_addresses(ri, interface_name)
+            fip_statuses = ri.configure_fip_addresses(interface_name)
 
         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)
+                fip_statuses = ri.put_fips_in_error_state()
 
         self._update_fip_statuses(ri, existing_floating_ips, fip_statuses)
 
@@ -589,7 +567,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
-        ex_gw_port = self._get_ex_gw_port(ri)
+        ex_gw_port = ri.get_ex_gw_port()
         if ri.router.get('distributed') and ex_gw_port:
             ri.fip_ns = self.get_fip_ns(ex_gw_port['network_id'])
             ri.fip_ns.scan_fip_ports(ri)
@@ -641,28 +619,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
                     break
         iptables_manager.apply()
 
-    def process_router_floating_ip_nat_rules(self, ri):
-        """Configure NAT rules for the router's floating IPs.
-
-        Configures iptables rules for the floating ips of the given router
-        """
-        # Clear out all iptables rules for floating ips
-        ri.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip')
-
-        floating_ips = self.get_floating_ips(ri)
-        # Loop once to ensure that floating ips are configured.
-        for fip in floating_ips:
-            # Rebuild iptables rules for the floating ip.
-            fixed = fip['fixed_ip_address']
-            fip_ip = fip['floating_ip_address']
-            for chain, rule in self.floating_forward_rules(fip_ip, fixed):
-                ri.iptables_manager.ipv4['nat'].add_rule(chain, rule,
-                                                         tag='floating_ip')
-
-        ri.iptables_manager.apply()
-
     def create_dvr_fip_interfaces(self, ri, ex_gw_port):
-        floating_ips = self.get_floating_ips(ri)
+        floating_ips = ri.get_floating_ips()
         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: "
@@ -692,91 +650,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
         else:
             return self.get_external_device_name(ex_gw_port['id'])
 
-    def _add_floating_ip(self, ri, fip, interface_name, device):
-        fip_ip = fip['floating_ip_address']
-        ip_cidr = common_utils.ip_to_cidr(fip_ip)
-
-        if ri.is_ha:
-            ri._add_vip(ip_cidr, interface_name)
-        else:
-            net = netaddr.IPNetwork(ip_cidr)
-            try:
-                device.addr.add(net.version, ip_cidr, str(net.broadcast))
-            except RuntimeError:
-                # any exception occurred here should cause the floating IP
-                # to be set in error state
-                LOG.warn(_LW("Unable to configure IP address for "
-                             "floating IP: %s"), fip['id'])
-                return l3_constants.FLOATINGIP_STATUS_ERROR
-            if ri.router['distributed']:
-                # Special Handling for DVR - update FIP namespace
-                # and ri.namespace to handle DVR based FIP
-                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.
-                ip_lib.send_gratuitous_arp(ri.ns_name,
-                                           interface_name,
-                                           fip_ip,
-                                           self.conf.send_arp_for_ha)
-            return l3_constants.FLOATINGIP_STATUS_ACTIVE
-
-    def _remove_floating_ip(self, ri, device, ip_cidr):
-        if ri.is_ha:
-            ri._remove_vip(ip_cidr)
-        else:
-            net = netaddr.IPNetwork(ip_cidr)
-            device.addr.delete(net.version, ip_cidr)
-            self.driver.delete_conntrack_state(namespace=ri.ns_name,
-                                               ip=ip_cidr)
-            if ri.router['distributed']:
-                ri.floating_ip_removed_dist(ip_cidr)
-
-    def _get_router_cidrs(self, ri, device):
-        if ri.is_ha:
-            return set(ri._ha_get_existing_cidrs(device.name))
-        else:
-            return set([addr['cidr'] for addr in device.addr.list()])
-
-    def process_router_floating_ip_addresses(self, ri, interface_name):
-        """Configure IP addresses on router's external gateway interface.
-
-        Ensures addresses for existing floating IPs and cleans up
-        those that should not longer be configured.
-        """
-
-        fip_statuses = {}
-        if interface_name is None:
-            LOG.debug('No Interface for floating IPs router: %s',
-                      ri.router['id'])
-            return fip_statuses
-
-        device = ip_lib.IPDevice(interface_name, namespace=ri.ns_name)
-        existing_cidrs = self._get_router_cidrs(ri, device)
-        new_cidrs = set()
-
-        floating_ips = self.get_floating_ips(ri)
-        # Loop once to ensure that floating ips are configured.
-        for fip in floating_ips:
-            fip_ip = fip['floating_ip_address']
-            ip_cidr = common_utils.ip_to_cidr(fip_ip)
-            new_cidrs.add(ip_cidr)
-            fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ACTIVE
-            if ip_cidr not in existing_cidrs:
-                fip_statuses[fip['id']] = self._add_floating_ip(
-                    ri, fip, interface_name, device)
-
-        fips_to_remove = (
-            ip_cidr for ip_cidr in existing_cidrs - new_cidrs
-            if common_utils.is_cidr_host(ip_cidr))
-        for ip_cidr in fips_to_remove:
-            self._remove_floating_ip(ri, device, ip_cidr)
-
-        return fip_statuses
-
-    def _get_ex_gw_port(self, ri):
-        return ri.router.get('gw_port')
-
     def get_internal_device_name(self, port_id):
         return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]
 
@@ -789,13 +662,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
     def get_router_id(self, ns_name):
         return ns_name[len(NS_PREFIX):]
 
-    def get_floating_ips(self, ri):
-        """Filter Floating IPs to be hosted on this agent."""
-        floating_ips = ri.router.get(l3_constants.FLOATINGIP_KEY, [])
-        if ri.router['distributed']:
-            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, [])
@@ -829,7 +695,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
         # Compute a list of addresses this router is supposed to have.
         # This avoids unnecessarily removing those addresses and
         # causing a momentarily network outage.
-        floating_ips = self.get_floating_ips(ri)
+        floating_ips = ri.get_floating_ips()
         preserve_ips = [common_utils.ip_to_cidr(ip['floating_ip_address'])
                         for ip in floating_ips]
 
@@ -852,7 +718,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
                 return
         else:
             ns_name = ri.ns_name
-            floating_ips = self.get_floating_ips(ri)
+            floating_ips = ri.get_floating_ips()
             preserve_ips = [common_utils.ip_to_cidr(ip['floating_ip_address'])
                             for ip in floating_ips]
 
@@ -886,12 +752,12 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
 
     def external_gateway_removed(self, ri, ex_gw_port, interface_name):
         if ri.router['distributed']:
-            self.process_router_floating_ip_nat_rules(ri)
+            # TODO(Carl) Should this be calling process_snat_dnat_for_fip?
+            ri.process_floating_ip_nat_rules()
             if ri.fip_ns:
                 to_fip_interface_name = (
                     self._get_external_device_interface_name(ri, ex_gw_port))
-                self.process_router_floating_ip_addresses(
-                    ri, to_fip_interface_name)
+                ri.process_floating_ip_addresses(to_fip_interface_name)
             for p in ri.internal_ports:
                 internal_interface = self.get_internal_device_name(p['id'])
                 self._snat_redirect_remove(ri, p, internal_interface)
@@ -959,7 +825,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
             ri._ha_disable_addressing_on_interface(interface_name)
             ri._add_vip(internal_cidr, interface_name)
 
-        ex_gw_port = self._get_ex_gw_port(ri)
+        ex_gw_port = ri.get_ex_gw_port()
         if ri.router['distributed'] and ex_gw_port:
             snat_ports = self.get_snat_interfaces(ri)
             sn_port = self._map_internal_interfaces(ri, port, snat_ports)
@@ -1007,14 +873,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
             self.driver.unplug(interface_name, namespace=ri.ns_name,
                                prefix=INTERNAL_DEV_PREFIX)
 
-    def floating_forward_rules(self, floating_ip, fixed_ip):
-        return [('PREROUTING', '-d %s -j DNAT --to %s' %
-                 (floating_ip, fixed_ip)),
-                ('OUTPUT', '-d %s -j DNAT --to %s' %
-                 (floating_ip, fixed_ip)),
-                ('float-snat', '-s %s -j SNAT --to %s' %
-                 (fixed_ip, floating_ip))]
-
     def router_deleted(self, context, router_id):
         """Deal with router deletion RPC message."""
         LOG.debug('Got router deleted notification for %s', router_id)
@@ -1239,7 +1097,7 @@ class L3NATAgentWithStateReport(L3NATAgent):
         router_infos = self.router_info.values()
         num_routers = len(router_infos)
         for ri in router_infos:
-            ex_gw_port = self._get_ex_gw_port(ri)
+            ex_gw_port = ri.get_ex_gw_port()
             if ex_gw_port:
                 num_ex_gw_ports += 1
             num_interfaces += len(ri.router.get(l3_constants.INTERFACE_KEY,
index f2adc4759bf3671291f9b072dcd2a71dfc022e85..b8532649507d07c2e4804868848e4438929a9c89 100644 (file)
 from neutron.agent.l3 import dvr_fip_ns
 from neutron.agent.l3 import router_info as router
 from neutron.agent.linux import ip_lib
+from neutron.common import constants as l3_constants
+from neutron.common import utils as common_utils
 
 
 class DvrRouter(router.RouterInfo):
-    def __init__(self, *args, **kwargs):
+    def __init__(self, host, *args, **kwargs):
         super(DvrRouter, self).__init__(*args, **kwargs)
 
+        self.host = host
+
         self.floating_ips_dict = {}
         self.snat_iptables_manager = None
         # Linklocal subnet for router and floating IP namespace link
         self.rtr_fip_subnet = None
         self.dist_fip_count = None
 
+    def get_floating_ips(self):
+        """Filter Floating IPs to be hosted on this agent."""
+        floating_ips = super(DvrRouter, self).get_floating_ips()
+        return [i for i in floating_ips if i['host'] == self.host]
+
     def _handle_fip_nat_rules(self, interface_name, action):
         """Configures NAT rules for Floating IPs for DVR.
 
@@ -114,3 +123,17 @@ class DvrRouter(router.RouterInfo):
                 # semaphore to sync creation/deletion of this namespace.
                 self.fip_ns.destroy()
                 self.fip_ns = None
+
+    def add_floating_ip(self, fip, interface_name, device):
+        if not self._add_fip_addr_to_device(fip, device):
+            return l3_constants.FLOATINGIP_STATUS_ERROR
+
+        # Special Handling for DVR - update FIP namespace
+        # and ri.namespace to handle DVR based FIP
+        ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address'])
+        self.floating_ip_added_dist(fip, ip_cidr)
+        return l3_constants.FLOATINGIP_STATUS_ACTIVE
+
+    def remove_floating_ip(self, device, ip_cidr):
+        super(DvrRouter, self).remove_floating_ip(device, ip_cidr)
+        self.floating_ip_removed_dist(ip_cidr)
index 10314d7571b38752c3d84c63af903d39884a7cd7..f4cafb933dee1c40e4ff974c298eb960a4424666 100644 (file)
@@ -20,6 +20,7 @@ from neutron.agent.l3 import router_info as router
 from neutron.agent.linux import ip_lib
 from neutron.agent.linux import keepalived
 from neutron.agent.metadata import driver as metadata_driver
+from neutron.common import utils as common_utils
 from neutron.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
@@ -164,6 +165,9 @@ class HaRouter(router.RouterInfo):
         instance = self._get_keepalived_instance()
         return instance.get_existing_vip_ip_addresses(interface_name)
 
+    def get_router_cidrs(self, device):
+        return set(self._ha_get_existing_cidrs(device.name))
+
     def _ha_external_gateway_removed(self, interface_name):
         self._clear_vips(interface_name)
 
@@ -238,3 +242,13 @@ class HaRouter(router.RouterInfo):
         old_gateway_cidr = self.ex_gw_port['ip_cidr']
         self._remove_vip(old_gateway_cidr)
         self._ha_external_gateway_added(ex_gw_port, interface_name)
+
+    def add_floating_ip(self, fip, interface_name, device):
+        fip_ip = fip['floating_ip_address']
+        ip_cidr = common_utils.ip_to_cidr(fip_ip)
+        self._add_vip(ip_cidr, interface_name)
+        # TODO(Carl) Should this return status?
+        # return l3_constants.FLOATINGIP_STATUS_ACTIVE
+
+    def remove_floating_ip(self, device, ip_cidr):
+        self._remove_vip(ip_cidr)
index 31dd0f326198e39973d4f100e2788e519706cfff..9c7c5bdc7e96074b11d0eab27bd6eb048785477b 100644 (file)
 #    under the License.
 
 from neutron.agent.l3 import router_info as router
+from neutron.agent.linux import ip_lib
+from neutron.common import constants as l3_constants
 
 
 class LegacyRouter(router.RouterInfo):
-    pass
+    def add_floating_ip(self, fip, interface_name, device):
+        if not self._add_fip_addr_to_device(fip, device):
+            return l3_constants.FLOATINGIP_STATUS_ERROR
+
+        # As GARP is processed in a distinct thread the call below
+        # won't raise an exception to be handled.
+        ip_lib.send_gratuitous_arp(self.ns_name,
+                                   interface_name,
+                                   fip['floating_ip_address'],
+                                   self.agent_conf.send_arp_for_ha)
+        return l3_constants.FLOATINGIP_STATUS_ACTIVE
index 3c7a5e7dffe7deedc75215191d8c9a6249106efe..464bf618599972f868cb0167324329c84335d327 100644 (file)
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import netaddr
+
 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 exceptions as n_exc
 from neutron.common import utils as common_utils
+from neutron.i18n import _LW
 from neutron.openstack.common import log as logging
 
 LOG = logging.getLogger(__name__)
@@ -103,3 +108,121 @@ class RouterInfo(object):
             LOG.debug("Removed route entry is '%s'", route)
             self._update_routing_table('delete', route)
         self.routes = new_routes
+
+    def get_ex_gw_port(self):
+        return self.router.get('gw_port')
+
+    def get_floating_ips(self):
+        """Filter Floating IPs to be hosted on this agent."""
+        return self.router.get(l3_constants.FLOATINGIP_KEY, [])
+
+    def floating_forward_rules(self, floating_ip, fixed_ip):
+        return [('PREROUTING', '-d %s -j DNAT --to %s' %
+                 (floating_ip, fixed_ip)),
+                ('OUTPUT', '-d %s -j DNAT --to %s' %
+                 (floating_ip, fixed_ip)),
+                ('float-snat', '-s %s -j SNAT --to %s' %
+                 (fixed_ip, floating_ip))]
+
+    def process_floating_ip_nat_rules(self):
+        """Configure NAT rules for the router's floating IPs.
+
+        Configures iptables rules for the floating ips of the given router
+        """
+        # Clear out all iptables rules for floating ips
+        self.iptables_manager.ipv4['nat'].clear_rules_by_tag('floating_ip')
+
+        floating_ips = self.get_floating_ips()
+        # Loop once to ensure that floating ips are configured.
+        for fip in floating_ips:
+            # Rebuild iptables rules for the floating ip.
+            fixed = fip['fixed_ip_address']
+            fip_ip = fip['floating_ip_address']
+            for chain, rule in self.floating_forward_rules(fip_ip, fixed):
+                self.iptables_manager.ipv4['nat'].add_rule(chain, rule,
+                                                           tag='floating_ip')
+
+        self.iptables_manager.apply()
+
+    def process_snat_dnat_for_fip(self):
+        try:
+            self.process_floating_ip_nat_rules()
+        except Exception:
+            # TODO(salv-orlando): Less broad catching
+            raise n_exc.FloatingIpSetupException(
+                'L3 agent failure to setup NAT for floating IPs')
+
+    def _add_fip_addr_to_device(self, fip, device):
+        """Configures the floating ip address on the device.
+        """
+        try:
+            ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address'])
+            net = netaddr.IPNetwork(ip_cidr)
+            device.addr.add(net.version, ip_cidr, str(net.broadcast))
+            return True
+        except RuntimeError:
+            # any exception occurred here should cause the floating IP
+            # to be set in error state
+            LOG.warn(_LW("Unable to configure IP address for "
+                         "floating IP: %s"), fip['id'])
+
+    def add_floating_ip(self, fip, interface_name, device):
+        raise NotImplementedError()
+
+    def remove_floating_ip(self, device, ip_cidr):
+        net = netaddr.IPNetwork(ip_cidr)
+        device.addr.delete(net.version, ip_cidr)
+        self.driver.delete_conntrack_state(namespace=self.ns_name, ip=ip_cidr)
+
+    def get_router_cidrs(self, device):
+        return set([addr['cidr'] for addr in device.addr.list()])
+
+    def process_floating_ip_addresses(self, interface_name):
+        """Configure IP addresses on router's external gateway interface.
+
+        Ensures addresses for existing floating IPs and cleans up
+        those that should not longer be configured.
+        """
+
+        fip_statuses = {}
+        if interface_name is None:
+            LOG.debug('No Interface for floating IPs router: %s',
+                      self.router['id'])
+            return fip_statuses
+
+        device = ip_lib.IPDevice(interface_name, namespace=self.ns_name)
+        existing_cidrs = self.get_router_cidrs(device)
+        new_cidrs = set()
+
+        floating_ips = self.get_floating_ips()
+        # Loop once to ensure that floating ips are configured.
+        for fip in floating_ips:
+            fip_ip = fip['floating_ip_address']
+            ip_cidr = common_utils.ip_to_cidr(fip_ip)
+            new_cidrs.add(ip_cidr)
+            fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ACTIVE
+            if ip_cidr not in existing_cidrs:
+                fip_statuses[fip['id']] = self.add_floating_ip(
+                    fip, interface_name, device)
+
+        fips_to_remove = (
+            ip_cidr for ip_cidr in existing_cidrs - new_cidrs
+            if common_utils.is_cidr_host(ip_cidr))
+        for ip_cidr in fips_to_remove:
+            self.remove_floating_ip(device, ip_cidr)
+
+        return fip_statuses
+
+    def configure_fip_addresses(self, interface_name):
+        try:
+            return self.process_floating_ip_addresses(interface_name)
+        except Exception:
+            # TODO(salv-orlando): Less broad catching
+            raise n_exc.FloatingIpSetupException('L3 agent failure to setup '
+                'floating IPs')
+
+    def put_fips_in_error_state(self):
+        fip_statuses = {}
+        for fip in self.router.get(l3_constants.FLOATINGIP_KEY, []):
+            fip_statuses[fip['id']] = l3_constants.FLOATINGIP_STATUS_ERROR
+        return fip_statuses
index 5e46b74c13d73f61c1e117f105f89e7d6fbf8a78..41235eb453e7c32a9b0740c699289111ceb272c4 100755 (executable)
@@ -148,7 +148,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
         router_id = router.router_id
         ha_device_name = router.get_ha_device_name(router.ha_port['id'])
         ha_device_cidr = router.ha_port['ip_cidr']
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         ex_port_ipv6 = router._get_ipv6_lladdr(
             external_port['mac_address'])
         external_device_name = self.agent.get_external_device_name(
@@ -161,7 +161,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
             internal_port['id'])
         internal_device_cidr = internal_port['ip_cidr']
         floating_ip_cidr = common_utils.ip_to_cidr(
-            self.agent.get_floating_ips(router)[0]['floating_ip_address'])
+            router.get_floating_ips()[0]['floating_ip_address'])
         default_gateway_ip = external_port['subnet'].get('gateway_ip')
 
         return """vrrp_instance VR_1 {
@@ -370,7 +370,7 @@ class L3AgentTestCase(L3AgentTestFramework):
         self.assertIn(new_fip, new_config)
         self.assertNotIn(old_gw, new_config)
         self.assertIn(new_gw, new_config)
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         external_device_name = self.agent.get_external_device_name(
             external_port['id'])
         self.assertNotIn('%s/24 dev %s' %
@@ -385,7 +385,7 @@ class L3AgentTestCase(L3AgentTestFramework):
         router = self.manage_router(self.agent, router_info)
 
         if enable_ha:
-            port = self.agent._get_ex_gw_port(router)
+            port = router.get_ex_gw_port()
             interface_name = self.agent.get_external_device_name(port['id'])
             self._assert_no_ip_addresses_on_interface(router, interface_name)
             helpers.wait_until_true(lambda: router.ha_state == 'master')
@@ -425,13 +425,13 @@ class L3AgentTestCase(L3AgentTestFramework):
             self.assertFalse(router.keepalived_manager.process.active)
 
     def _assert_external_device(self, router):
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         self.assertTrue(self.device_exists_with_ip_mac(
             external_port, self.agent.get_external_device_name,
             router.ns_name))
 
     def _assert_gateway(self, router):
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         external_device_name = self.agent.get_external_device_name(
             external_port['id'])
         external_device = ip_lib.IPDevice(external_device_name,
@@ -444,7 +444,7 @@ class L3AgentTestCase(L3AgentTestFramework):
 
     def _floating_ips_configured(self, router):
         floating_ips = router.router[l3_constants.FLOATINGIP_KEY]
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         return len(floating_ips) and all(ip_lib.device_exists_with_ip_mac(
             self.agent.get_external_device_name(external_port['id']),
             '%s/32' % fip['floating_ip_address'],
@@ -708,7 +708,7 @@ class TestDvrRouter(L3AgentTestFramework):
             ]
 
     def _assert_dvr_external_device(self, router):
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         snat_ns_name = self.agent.get_snat_ns_name(router.router_id)
 
         # if the agent is in dvr_snat mode, then we have to check
@@ -746,7 +746,7 @@ class TestDvrRouter(L3AgentTestFramework):
 
     def _assert_dvr_snat_gateway(self, router):
         namespace = self.agent.get_snat_ns_name(router.router_id)
-        external_port = self.agent._get_ex_gw_port(router)
+        external_port = router.get_ex_gw_port()
         external_device_name = self.agent.get_external_device_name(
             external_port['id'])
         external_device = ip_lib.IPDevice(external_device_name,
similarity index 68%
rename from neutron/tests/unit/test_dvr_router.py
rename to neutron/tests/unit/agent/l3/test_dvr_router.py
index 2f3aa76ce92857c4a9cc3637200947d1148da48a..9c05b6535d64334fcf48ee98c9fc5b2aed71fe78 100644 (file)
@@ -17,7 +17,9 @@ import netaddr
 
 from neutron.agent.l3 import dvr_router
 from neutron.agent.l3 import link_local_allocator as lla
+from neutron.agent.l3 import router_info
 from neutron.agent.linux import ip_lib
+from neutron.common import constants as l3_constants
 from neutron.common import utils as common_utils
 from neutron.openstack.common import uuidutils
 from neutron.tests import base
@@ -33,12 +35,23 @@ class TestDvrRouterOperations(base.BaseTestCase):
 
     def _create_router(self, router, **kwargs):
         agent_conf = mock.Mock()
-        return dvr_router.DvrRouter(mock.sentinel.router_id,
+        return dvr_router.DvrRouter(mock.sentinel.myhost,
+                                    mock.sentinel.router_id,
                                     router,
                                     agent_conf,
                                     mock.sentinel.interface_driver,
                                     **kwargs)
 
+    def test_get_floating_ips_dvr(self):
+        router = mock.MagicMock()
+        router.get.return_value = [{'host': mock.sentinel.myhost},
+                                   {'host': mock.sentinel.otherhost}]
+        ri = self._create_router(router)
+
+        fips = ri.get_floating_ips()
+
+        self.assertEqual([{'host': mock.sentinel.myhost}], fips)
+
     @mock.patch.object(ip_lib, 'send_garp_for_proxyarp')
     @mock.patch.object(ip_lib, 'IPDevice')
     @mock.patch.object(ip_lib, 'IpRule')
@@ -115,3 +128,41 @@ class TestDvrRouterOperations(base.BaseTestCase):
         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)
+        ri.floating_ip_added_dist = mock.Mock()
+
+        result = ri.add_floating_ip(fip,
+                                    mock.sentinel.interface_name,
+                                    mock.sentinel.device)
+        ri._add_fip_addr_to_device.assert_called_once_with(
+            fip, mock.sentinel.device)
+        return result
+
+    def test_add_floating_ip(self):
+        ri = self._create_router(mock.MagicMock())
+        ip = '15.1.2.3'
+        fip = {'floating_ip_address': ip}
+        result = self._test_add_floating_ip(ri, fip, True)
+        ri.floating_ip_added_dist.assert_called_once_with(fip, ip + '/32')
+        self.assertEqual(l3_constants.FLOATINGIP_STATUS_ACTIVE, result)
+
+    def test_add_floating_ip_error(self):
+        ri = self._create_router(mock.MagicMock())
+        result = self._test_add_floating_ip(
+            ri, {'floating_ip_address': '15.1.2.3'}, False)
+        self.assertFalse(ri.floating_ip_added_dist.called)
+        self.assertEqual(l3_constants.FLOATINGIP_STATUS_ERROR, result)
+
+    @mock.patch.object(router_info.RouterInfo, 'remove_floating_ip')
+    def test_remove_floating_ip(self, super_remove_floating_ip):
+        ri = self._create_router(mock.MagicMock())
+        ri.floating_ip_removed_dist = mock.Mock()
+
+        ri.remove_floating_ip(mock.sentinel.device, mock.sentinel.ip_cidr)
+
+        super_remove_floating_ip.assert_called_once_with(
+            mock.sentinel.device, mock.sentinel.ip_cidr)
+        ri.floating_ip_removed_dist.assert_called_once_with(
+            mock.sentinel.ip_cidr)
diff --git a/neutron/tests/unit/agent/l3/test_ha_router.py b/neutron/tests/unit/agent/l3/test_ha_router.py
new file mode 100644 (file)
index 0000000..1800936
--- /dev/null
@@ -0,0 +1,41 @@
+# 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
+
+from neutron.agent.l3 import ha_router
+from neutron.tests import base
+
+
+class TestBasicRouterOperations(base.BaseTestCase):
+    def setUp(self):
+        super(TestBasicRouterOperations, self).setUp()
+
+    def _create_router(self, router=None, **kwargs):
+        if not router:
+            router = mock.MagicMock()
+        return ha_router.HaRouter(mock.sentinel.router_id,
+                                  router,
+                                  mock.sentinel.agent_conf,
+                                  mock.sentinel.driver,
+                                  ns_name=mock.sentinel.namespace,
+                                  **kwargs)
+
+    def test_get_router_cidrs_returns_ha_cidrs(self):
+        ri = self._create_router()
+        device = mock.MagicMock()
+        device.name.return_value = 'eth2'
+        addresses = ['15.1.2.2/24', '15.1.2.3/32']
+        ri._ha_get_existing_cidrs = mock.MagicMock(return_value=addresses)
+        self.assertEqual(set(addresses), ri.get_router_cidrs(device))
diff --git a/neutron/tests/unit/agent/l3/test_l3_router.py b/neutron/tests/unit/agent/l3/test_l3_router.py
new file mode 100644 (file)
index 0000000..66e8ad8
--- /dev/null
@@ -0,0 +1,222 @@
+# 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
+
+from neutron.agent.l3 import router_info
+from neutron.agent.linux import ip_lib
+from neutron.common import constants as l3_constants
+from neutron.common import exceptions as n_exc
+from neutron.openstack.common import uuidutils
+from neutron.tests import base
+
+_uuid = uuidutils.generate_uuid
+
+
+class BasicRouterTestCaseFramework(base.BaseTestCase):
+    def _create_router(self, router=None, **kwargs):
+        if not router:
+            router = mock.MagicMock()
+        return router_info.RouterInfo(mock.sentinel.router_id,
+                                      router,
+                                      mock.sentinel.agent_conf,
+                                      mock.sentinel.interface_driver,
+                                      **kwargs)
+
+
+class TestBasicRouterOperations(BasicRouterTestCaseFramework):
+
+    def test_get_floating_ips(self):
+        router = mock.MagicMock()
+        router.get.return_value = [mock.sentinel.floating_ip]
+        ri = self._create_router(router)
+
+        fips = ri.get_floating_ips()
+
+        self.assertEqual([mock.sentinel.floating_ip], fips)
+
+    def test_process_floating_ip_nat_rules(self):
+        ri = self._create_router()
+        fips = [{'fixed_ip_address': mock.sentinel.ip,
+                 'floating_ip_address': mock.sentinel.fip}]
+        ri.get_floating_ips = mock.Mock(return_value=fips)
+        ri.iptables_manager = mock.MagicMock()
+        ipv4_nat = ri.iptables_manager.ipv4['nat']
+        ri.floating_forward_rules = mock.Mock(
+            return_value=[(mock.sentinel.chain, mock.sentinel.rule)])
+
+        ri.process_floating_ip_nat_rules()
+
+        # Be sure that the rules are cleared first and apply is called last
+        self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'),
+                         ipv4_nat.mock_calls[0])
+        self.assertEqual(mock.call.apply(), ri.iptables_manager.mock_calls[-1])
+
+        # Be sure that add_rule is called somewhere in the middle
+        ipv4_nat.add_rule.assert_called_once_with(mock.sentinel.chain,
+                                                  mock.sentinel.rule,
+                                                  tag='floating_ip')
+
+    def test_process_floating_ip_nat_rules_removed(self):
+        ri = self._create_router()
+        ri.get_floating_ips = mock.Mock(return_value=[])
+        ri.iptables_manager = mock.MagicMock()
+        ipv4_nat = ri.iptables_manager.ipv4['nat']
+
+        ri.process_floating_ip_nat_rules()
+
+        # Be sure that the rules are cleared first and apply is called last
+        self.assertEqual(mock.call.clear_rules_by_tag('floating_ip'),
+                         ipv4_nat.mock_calls[0])
+        self.assertEqual(mock.call.apply(), ri.iptables_manager.mock_calls[-1])
+
+        # Be sure that add_rule is called somewhere in the middle
+        self.assertFalse(ipv4_nat.add_rule.called)
+
+    def _test_add_fip_addr_to_device_error(self, device):
+        ri = self._create_router()
+        ip = '15.1.2.3'
+
+        result = ri._add_fip_addr_to_device(
+            {'id': mock.sentinel.id, 'floating_ip_address': ip}, device)
+
+        device.addr.add.assert_called_with(4, ip + '/32', ip)
+        return result
+
+    def test__add_fip_addr_to_device(self):
+        result = self._test_add_fip_addr_to_device_error(mock.Mock())
+        self.assertTrue(result)
+
+    def test__add_fip_addr_to_device_error(self):
+        device = mock.Mock()
+        device.addr.add.side_effect = RuntimeError
+        result = self._test_add_fip_addr_to_device_error(device)
+        self.assertFalse(result)
+
+    def test_process_snat_dnat_for_fip(self):
+        ri = self._create_router()
+        ri.process_floating_ip_nat_rules = mock.Mock(side_effect=Exception)
+
+        self.assertRaises(n_exc.FloatingIpSetupException,
+                          ri.process_snat_dnat_for_fip)
+
+        ri.process_floating_ip_nat_rules.assert_called_once_with()
+
+    def test_put_fips_in_error_state(self):
+        ri = self._create_router()
+        ri.router = mock.Mock()
+        ri.router.get.return_value = [{'id': mock.sentinel.id1},
+                                      {'id': mock.sentinel.id2}]
+
+        statuses = ri.put_fips_in_error_state()
+
+        expected = [{mock.sentinel.id1: l3_constants.FLOATINGIP_STATUS_ERROR,
+                     mock.sentinel.id2: l3_constants.FLOATINGIP_STATUS_ERROR}]
+        self.assertNotEqual(expected, statuses)
+
+    def test_configure_fip_addresses(self):
+        ri = self._create_router()
+        ri.process_floating_ip_addresses = mock.Mock(
+            side_effect=Exception)
+
+        self.assertRaises(n_exc.FloatingIpSetupException,
+                          ri.configure_fip_addresses,
+                          mock.sentinel.interface_name)
+
+        ri.process_floating_ip_addresses.assert_called_once_with(
+            mock.sentinel.interface_name)
+
+    def test_get_router_cidrs_returns_cidrs(self):
+        ri = self._create_router()
+        addresses = ['15.1.2.2/24', '15.1.2.3/32']
+        device = mock.MagicMock()
+        device.addr.list.return_value = [{'cidr': addresses[0]},
+                                         {'cidr': addresses[1]}]
+        self.assertEqual(set(addresses), ri.get_router_cidrs(device))
+
+
+@mock.patch.object(ip_lib, 'IPDevice')
+class TestFloatingIpWithMockDevice(BasicRouterTestCaseFramework):
+
+    def test_process_floating_ip_addresses_remap(self, IPDevice):
+        fip_id = _uuid()
+        fip = {
+            'id': fip_id, 'port_id': _uuid(),
+            'floating_ip_address': '15.1.2.3',
+            'fixed_ip_address': '192.168.0.2'
+        }
+
+        IPDevice.return_value = device = mock.Mock()
+        device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}]
+        ri = self._create_router()
+        ri.get_floating_ips = mock.Mock(return_value=[fip])
+
+        fip_statuses = ri.process_floating_ip_addresses(
+            mock.sentinel.interface_name)
+        self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ACTIVE},
+                         fip_statuses)
+
+        self.assertFalse(device.addr.add.called)
+        self.assertFalse(device.addr.delete.called)
+
+    def test_process_router_with_disabled_floating_ip(self, IPDevice):
+        fip_id = _uuid()
+        fip = {
+            'id': fip_id, 'port_id': _uuid(),
+            'floating_ip_address': '15.1.2.3',
+            'fixed_ip_address': '192.168.0.2'
+        }
+
+        ri = self._create_router()
+        ri.floating_ips = [fip]
+        ri.get_floating_ips = mock.Mock(return_value=[])
+
+        fip_statuses = ri.process_floating_ip_addresses(
+            mock.sentinel.interface_name)
+
+        self.assertIsNone(fip_statuses.get(fip_id))
+
+    def test_process_router_floating_ip_with_device_add_error(self, IPDevice):
+        IPDevice.return_value = device = mock.Mock(side_effect=RuntimeError)
+        device.addr.list.return_value = []
+        fip_id = _uuid()
+        fip = {
+            'id': fip_id, 'port_id': _uuid(),
+            'floating_ip_address': '15.1.2.3',
+            'fixed_ip_address': '192.168.0.2'
+        }
+        ri = self._create_router()
+        ri.add_floating_ip = mock.Mock(
+            return_value=l3_constants.FLOATINGIP_STATUS_ERROR)
+        ri.get_floating_ips = mock.Mock(return_value=[fip])
+
+        fip_statuses = ri.process_floating_ip_addresses(
+            mock.sentinel.interface_name)
+
+        self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ERROR},
+                         fip_statuses)
+
+    # TODO(mrsmith): refactor for DVR cases
+    def test_process_floating_ip_addresses_remove(self, IPDevice):
+        IPDevice.return_value = device = mock.Mock()
+        device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}]
+
+        ri = self._create_router()
+        ri.remove_floating_ip = mock.Mock()
+        ri.router.get = mock.Mock(return_value=[])
+
+        fip_statuses = ri.process_floating_ip_addresses(
+            mock.sentinel.interface_name)
+        self.assertEqual({}, fip_statuses)
+        ri.remove_floating_ip.assert_called_once_with(device, '15.1.2.3/32')
diff --git a/neutron/tests/unit/agent/l3/test_legacy_router.py b/neutron/tests/unit/agent/l3/test_legacy_router.py
new file mode 100644 (file)
index 0000000..0857656
--- /dev/null
@@ -0,0 +1,77 @@
+# 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
+
+from neutron.agent.l3 import legacy_router
+from neutron.agent.linux import ip_lib
+from neutron.common import constants as l3_constants
+from neutron.tests import base
+
+
+class BasicRouterTestCaseFramework(base.BaseTestCase):
+    def _create_router(self, router=None, **kwargs):
+        if not router:
+            router = mock.MagicMock()
+        self.agent_conf = mock.Mock()
+        self.driver = mock.Mock()
+        return legacy_router.LegacyRouter(mock.sentinel.router_id,
+                                          router,
+                                          self.agent_conf,
+                                          self.driver,
+                                          ns_name=mock.sentinel.namespace,
+                                          **kwargs)
+
+
+class TestBasicRouterOperations(BasicRouterTestCaseFramework):
+
+    def test_remove_floating_ip(self):
+        ri = self._create_router(mock.MagicMock())
+        device = mock.Mock()
+        cidr = '15.1.2.3/32'
+
+        ri.remove_floating_ip(device, cidr)
+
+        device.addr.delete.assert_called_once_with(4, cidr)
+        self.driver.delete_conntrack_state.assert_called_once_with(
+            ip=cidr,
+            namespace=mock.sentinel.namespace)
+
+
+@mock.patch.object(ip_lib, 'send_gratuitous_arp')
+class TestAddFloatingIpWithMockGarp(BasicRouterTestCaseFramework):
+    def test_add_floating_ip(self, send_gratuitous_arp):
+        ri = self._create_router()
+        ri._add_fip_addr_to_device = mock.Mock(return_value=True)
+        self.agent_conf.send_arp_for_ha = mock.sentinel.arp_count
+        ri.ns_name = mock.sentinel.ns_name
+        ip = '15.1.2.3'
+        result = ri.add_floating_ip({'floating_ip_address': ip},
+                                    mock.sentinel.interface_name,
+                                    mock.sentinel.device)
+        ip_lib.send_gratuitous_arp.assert_called_once_with(
+            mock.sentinel.ns_name,
+            mock.sentinel.interface_name,
+            ip,
+            mock.sentinel.arp_count)
+        self.assertEqual(l3_constants.FLOATINGIP_STATUS_ACTIVE, result)
+
+    def test_add_floating_ip_error(self, send_gratuitous_arp):
+        ri = self._create_router()
+        ri._add_fip_addr_to_device = mock.Mock(return_value=False)
+        result = ri.add_floating_ip({'floating_ip_address': '15.1.2.3'},
+                                    mock.sentinel.interface_name,
+                                    mock.sentinel.device)
+        self.assertFalse(ip_lib.send_gratuitous_arp.called)
+        self.assertEqual(l3_constants.FLOATINGIP_STATUS_ERROR, result)
index e64a1ad6cd2670c5caaafba328b2102c9da82c8a..892e30c50f575a778fbcdf04db378791b1add3e6 100644 (file)
@@ -26,9 +26,9 @@ from neutron.agent.common import config as agent_config
 from neutron.agent.l3 import agent as l3_agent
 from neutron.agent.l3 import config as l3_config
 from neutron.agent.l3 import dvr
-from neutron.agent.l3 import dvr_fip_ns
 from neutron.agent.l3 import dvr_router
 from neutron.agent.l3 import ha
+from neutron.agent.l3 import legacy_router
 from neutron.agent.l3 import link_local_allocator as lla
 from neutron.agent.l3 import router_info as l3router
 from neutron.agent.linux import external_process
@@ -753,7 +753,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
 
     def test_process_dist_router(self):
         router = prepare_router_data()
-        ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs)
+        ri = dvr_router.DvrRouter(HOSTNAME,
+                                  router['id'],
+                                  router,
+                                  **self.ri_kwargs)
         subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0])
         ri.router['distributed'] = True
         ri.router['_snat_router_interfaces'] = [{
@@ -768,9 +771,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         agent.host = HOSTNAME
         fake_fip_id = 'fake_fip_id'
         agent.create_dvr_fip_interfaces = mock.Mock()
-        agent.process_router_floating_ip_addresses = mock.Mock()
-        agent.process_router_floating_ip_nat_rules = mock.Mock()
-        agent.process_router_floating_ip_addresses.return_value = {
+        ri.process_floating_ip_addresses = mock.Mock()
+        ri.process_floating_ip_nat_rules = mock.Mock()
+        ri.process_floating_ip_addresses.return_value = {
             fake_fip_id: 'ACTIVE'}
         agent.external_gateway_added = mock.Mock()
         agent.external_gateway_updated = mock.Mock()
@@ -781,11 +784,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
              'port_id': _uuid(),
              'host': HOSTNAME}]}
         agent.process_router(ri)
-        agent.process_router_floating_ip_addresses.assert_called_with(
-            ri, mock.ANY)
-        agent.process_router_floating_ip_addresses.reset_mock()
-        agent.process_router_floating_ip_nat_rules.assert_called_with(ri)
-        agent.process_router_floating_ip_nat_rules.reset_mock()
+        ri.process_floating_ip_addresses.assert_called_with(mock.ANY)
+        ri.process_floating_ip_addresses.reset_mock()
+        ri.process_floating_ip_nat_rules.assert_called_with()
+        ri.process_floating_ip_nat_rules.reset_mock()
         agent.external_gateway_added.reset_mock()
 
         # remap floating IP to a new fixed ip
@@ -794,11 +796,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
 
         router[l3_constants.FLOATINGIP_KEY] = fake_floatingips2['floatingips']
         agent.process_router(ri)
-        agent.process_router_floating_ip_addresses.assert_called_with(
-            ri, mock.ANY)
-        agent.process_router_floating_ip_addresses.reset_mock()
-        agent.process_router_floating_ip_nat_rules.assert_called_with(ri)
-        agent.process_router_floating_ip_nat_rules.reset_mock()
+        ri.process_floating_ip_addresses.assert_called_with(mock.ANY)
+        ri.process_floating_ip_addresses.reset_mock()
+        ri.process_floating_ip_nat_rules.assert_called_with()
+        ri.process_floating_ip_nat_rules.reset_mock()
         self.assertEqual(agent.external_gateway_added.call_count, 0)
         self.assertEqual(agent.external_gateway_updated.call_count, 0)
         agent.external_gateway_added.reset_mock()
@@ -812,19 +813,18 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         ri.router['gw_port']['fixed_ips'][0]['ip_address'] = str(old_ip + 1)
 
         agent.process_router(ri)
-        agent.process_router_floating_ip_addresses.reset_mock()
-        agent.process_router_floating_ip_nat_rules.reset_mock()
+        ri.process_floating_ip_addresses.reset_mock()
+        ri.process_floating_ip_nat_rules.reset_mock()
         self.assertEqual(agent.external_gateway_added.call_count, 0)
         self.assertEqual(agent.external_gateway_updated.call_count, 1)
 
         # remove just the floating ips
         del router[l3_constants.FLOATINGIP_KEY]
         agent.process_router(ri)
-        agent.process_router_floating_ip_addresses.assert_called_with(
-            ri, mock.ANY)
-        agent.process_router_floating_ip_addresses.reset_mock()
-        agent.process_router_floating_ip_nat_rules.assert_called_with(ri)
-        agent.process_router_floating_ip_nat_rules.reset_mock()
+        ri.process_floating_ip_addresses.assert_called_with(mock.ANY)
+        ri.process_floating_ip_addresses.reset_mock()
+        ri.process_floating_ip_nat_rules.assert_called_with()
+        ri.process_floating_ip_nat_rules.reset_mock()
 
         # now no ports so state is torn down
         del router[l3_constants.INTERFACE_KEY]
@@ -832,51 +832,32 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         agent.process_router(ri)
         self.assertEqual(self.send_arp.call_count, 1)
         distributed = ri.router.get('distributed', False)
-        self.assertEqual(agent.process_router_floating_ip_addresses.called,
+        self.assertEqual(ri.process_floating_ip_addresses.called,
                          distributed)
-        self.assertEqual(agent.process_router_floating_ip_nat_rules.called,
+        self.assertEqual(ri.process_floating_ip_nat_rules.called,
                          distributed)
 
     @mock.patch('neutron.agent.linux.ip_lib.IPDevice')
-    def _test_process_router_floating_ip_addresses_add(self, ri,
-                                                       agent, IPDevice):
-        floating_ips = agent.get_floating_ips(ri)
+    def _test_process_floating_ip_addresses_add(self, ri, agent, IPDevice):
+        floating_ips = ri.get_floating_ips()
         fip_id = floating_ips[0]['id']
         IPDevice.return_value = device = mock.Mock()
         device.addr.list.return_value = []
         ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
         ex_gw_port = {'id': _uuid(), 'network_id': mock.sentinel.ext_net_id}
 
+        ri.add_floating_ip = mock.Mock(
+            return_value=l3_constants.FLOATINGIP_STATUS_ACTIVE)
         with mock.patch.object(lla.LinkLocalAllocator, '_write'):
             if ri.router['distributed']:
                 ri.fip_ns = agent.get_fip_ns(ex_gw_port['network_id'])
                 agent.create_dvr_fip_interfaces(ri, ex_gw_port)
-            fip_statuses = agent.process_router_floating_ip_addresses(
-                ri, ex_gw_port)
+            fip_statuses = ri.process_floating_ip_addresses(
+                mock.sentinel.interface_name)
         self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ACTIVE},
                          fip_statuses)
-        device.addr.add.assert_called_once_with(4, '15.1.2.3/32', '15.1.2.3')
-
-    def test_process_router_floating_ip_nat_rules_add(self):
-        fip = {
-            'id': _uuid(), 'port_id': _uuid(),
-            'floating_ip_address': '15.1.2.3',
-            'fixed_ip_address': '192.168.0.1'
-        }
-
-        ri = mock.MagicMock()
-        ri.router['distributed'].__nonzero__ = lambda self: False
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        agent.get_floating_ips = mock.Mock(return_value=[fip])
-
-        agent.process_router_floating_ip_nat_rules(ri)
-
-        nat = ri.iptables_manager.ipv4['nat']
-        nat.clear_rules_by_tag.assert_called_once_with('floating_ip')
-        rules = agent.floating_forward_rules('15.1.2.3', '192.168.0.1')
-        for chain, rule in rules:
-            nat.add_rule.assert_any_call(chain, rule, tag='floating_ip')
+        ri.add_floating_ip.assert_called_once_with(
+            floating_ips[0], mock.sentinel.interface_name, device)
 
     def test_get_floating_agent_gw_interfaces(self):
         fake_network_id = _uuid()
@@ -895,7 +876,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         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)
+        ri = dvr_router.DvrRouter(
+            HOSTNAME, router['id'], router, **self.ri_kwargs)
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
         self.assertEqual(
             agent_gateway_port[0],
@@ -925,7 +907,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         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)
+        ri = dvr_router.DvrRouter(
+            HOSTNAME, router['id'], router, **self.ri_kwargs)
 
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
         ext_gw_port = ri.router.get('gw_port')
@@ -933,7 +916,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         ri.dist_fip_count = 0
         ri.fip_ns.subscribe = mock.Mock()
 
-        with contextlib.nested(mock.patch.object(agent,
+        with contextlib.nested(mock.patch.object(ri,
                                                  'get_floating_ips'),
                                mock.patch.object(
                                    agent, 'get_floating_agent_gw_interface'),
@@ -961,10 +944,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
 
         router = prepare_router_data(enable_snat=True)
         router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips']
-        ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
+        ri = legacy_router.LegacyRouter(router['id'], router, **self.ri_kwargs)
         ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        self._test_process_router_floating_ip_addresses_add(ri, agent)
+        agent.get_external_device_name = mock.Mock(return_value='exgw')
+        self._test_process_floating_ip_addresses_add(ri, agent)
 
     def test_process_router_dist_floating_ip_add(self):
         fake_floatingips = {'floatingips': [
@@ -984,11 +968,13 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         router = prepare_router_data(enable_snat=True)
         router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips']
         router['distributed'] = True
-        ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs)
+        ri = dvr_router.DvrRouter(HOSTNAME,
+                                  router['id'],
+                                  router,
+                                  **self.ri_kwargs)
         ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
         ri.dist_fip_count = 0
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        agent.host = HOSTNAME
         fip_ns = agent.get_fip_ns(mock.sentinel.ext_net_id)
         fip_ns.agent_gateway_port = (
             {'fixed_ips': [{'ip_address': '20.0.0.30',
@@ -999,131 +985,6 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
              'mac_address': 'ca:fe:de:ad:be:ef',
              'ip_cidr': '20.0.0.30/24'}
         )
-        self._test_process_router_floating_ip_addresses_add(ri, agent)
-
-    def test_get_router_cidrs_returns_cidrs(self):
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        ri = mock.MagicMock()
-        ri.is_ha = False
-        addresses = ['15.1.2.2/24', '15.1.2.3/32']
-        device = mock.MagicMock()
-        device.addr.list.return_value = [{'cidr': addresses[0]},
-                                         {'cidr': addresses[1]}]
-        self.assertEqual(set(addresses), agent._get_router_cidrs(ri, device))
-
-    def test_get_router_cidrs_returns_ha_cidrs(self):
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        ri = mock.MagicMock()
-        ri.is_ha = True
-        device = mock.MagicMock()
-        device.name.return_value = 'eth2'
-        addresses = ['15.1.2.2/24', '15.1.2.3/32']
-        ri._ha_get_existing_cidrs = mock.MagicMock()
-        ri._ha_get_existing_cidrs.return_value = addresses
-        self.assertEqual(set(addresses), agent._get_router_cidrs(ri, device))
-
-    # TODO(mrsmith): refactor for DVR cases
-    @mock.patch('neutron.agent.linux.ip_lib.IPDevice')
-    def test_process_router_floating_ip_addresses_remove(self, IPDevice):
-        IPDevice.return_value = device = mock.Mock()
-        device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}]
-
-        ri = mock.MagicMock()
-        ri.router.get.return_value = []
-        type(ri).is_ha = mock.PropertyMock(return_value=False)
-        ri.router['distributed'].__nonzero__ = lambda self: False
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-
-        fip_statuses = agent.process_router_floating_ip_addresses(
-            ri, {'id': _uuid()})
-        self.assertEqual({}, fip_statuses)
-        device.addr.delete.assert_called_once_with(4, '15.1.2.3/32')
-        self.mock_driver.delete_conntrack_state.assert_called_once_with(
-            namespace=ri.ns_name,
-            ip='15.1.2.3/32')
-
-    def test_process_router_floating_ip_nat_rules_remove(self):
-        ri = mock.MagicMock()
-        ri.router.get.return_value = []
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-
-        agent.process_router_floating_ip_nat_rules(ri)
-
-        nat = ri.iptables_manager.ipv4['nat']
-        nat = ri.iptables_manager.ipv4['nat`']
-        nat.clear_rules_by_tag.assert_called_once_with('floating_ip')
-
-    @mock.patch('neutron.agent.linux.ip_lib.IPDevice')
-    def test_process_router_floating_ip_addresses_remap(self, IPDevice):
-        fip_id = _uuid()
-        fip = {
-            'id': fip_id, 'port_id': _uuid(),
-            'floating_ip_address': '15.1.2.3',
-            'fixed_ip_address': '192.168.0.2'
-        }
-
-        IPDevice.return_value = device = mock.Mock()
-        device.addr.list.return_value = [{'cidr': '15.1.2.3/32'}]
-        ri = mock.MagicMock()
-        ri.router['distributed'].__nonzero__ = lambda self: False
-        type(ri).is_ha = mock.PropertyMock(return_value=False)
-        ri.router.get.return_value = [fip]
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-
-        fip_statuses = agent.process_router_floating_ip_addresses(
-            ri, {'id': _uuid()})
-        self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ACTIVE},
-                         fip_statuses)
-
-        self.assertFalse(device.addr.add.called)
-        self.assertFalse(device.addr.delete.called)
-
-    @mock.patch('neutron.agent.linux.ip_lib.IPDevice')
-    def test_process_router_with_disabled_floating_ip(self, IPDevice):
-        fip_id = _uuid()
-        fip = {
-            'id': fip_id, 'port_id': _uuid(),
-            'floating_ip_address': '15.1.2.3',
-            'fixed_ip_address': '192.168.0.2'
-        }
-
-        ri = mock.MagicMock()
-        ri.floating_ips = [fip]
-        ri.router.get.return_value = []
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-
-        fip_statuses = agent.process_router_floating_ip_addresses(
-            ri, {'id': _uuid(), 'network_id': mock.sentinel.ext_net_id})
-
-        self.assertIsNone(fip_statuses.get(fip_id))
-
-    @mock.patch('neutron.agent.linux.ip_lib.IPDevice')
-    def test_process_router_floating_ip_with_device_add_error(self, IPDevice):
-        IPDevice.return_value = device = mock.Mock()
-        device.addr.add.side_effect = RuntimeError()
-        device.addr.list.return_value = []
-        fip_id = _uuid()
-        fip = {
-            'id': fip_id, 'port_id': _uuid(),
-            'floating_ip_address': '15.1.2.3',
-            'fixed_ip_address': '192.168.0.2'
-        }
-        ri = mock.MagicMock()
-        type(ri).is_ha = mock.PropertyMock(return_value=False)
-        ri.router.get.return_value = [fip]
-        ri.router['distributed'].__nonzero__ = lambda self: False
-
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-
-        fip_statuses = agent.process_router_floating_ip_addresses(
-            ri, {'id': _uuid()})
-
-        self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ERROR},
-                         fip_statuses)
 
     def test_process_router_snat_disabled(self):
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
@@ -1386,7 +1247,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
                  'fixed_ip_address': '7.7.7.7',
                  'port_id': router[l3_constants.INTERFACE_KEY][0]['id']}]
 
-            ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
+            ri = legacy_router.LegacyRouter(router['id'],
+                                            router,
+                                            **self.ri_kwargs)
             agent.external_gateway_added = mock.Mock()
             agent.process_router(ri)
             # Assess the call for putting the floating IP up was performed
@@ -1405,8 +1268,6 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
 
     def test_process_router_floatingip_exception(self):
         agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        agent.process_router_floating_ip_addresses = mock.Mock()
-        agent.process_router_floating_ip_addresses.side_effect = RuntimeError
         with mock.patch.object(
             agent.plugin_rpc,
             'update_floatingip_statuses') as mock_update_fip_status:
@@ -1419,6 +1280,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
                  'port_id': router[l3_constants.INTERFACE_KEY][0]['id']}]
 
             ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs)
+            ri.process_floating_ip_addresses = mock.Mock(
+                side_effect=RuntimeError)
             agent.external_gateway_added = mock.Mock()
             agent.process_router(ri)
             # Assess the call for putting the floating IP into Error
@@ -1429,6 +1292,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
 
     def test_handle_router_snat_rules_distributed_without_snat_manager(self):
         ri = dvr_router.DvrRouter(
+            HOSTNAME,
             'foo_router_id',
             {'distributed': True},
             **self.ri_kwargs)
@@ -1892,7 +1756,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
         self.mock_driver.unplug.reset_mock()
 
         external_net_id = router['gw_port']['network_id']
-        ri = dvr_router.DvrRouter(router['id'], router, **self.ri_kwargs)
+        ri = dvr_router.DvrRouter(
+            HOSTNAME, router['id'], router, **self.ri_kwargs)
+        ri.remove_floating_ip = mock.Mock()
         agent._fetch_external_net_id = mock.Mock(return_value=external_net_id)
         ri.ex_gw_port = ri.router['gw_port']
         del ri.router['gw_port']
@@ -1929,24 +1795,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
             ri, ri.ex_gw_port,
             agent.get_external_device_name(ri.ex_gw_port['id']))
 
-        self.assertFalse(nat.add_rule.called)
-        nat.clear_rules_by_tag.assert_called_once_with('floating_ip')
         if fip_ns:
-            self.mock_ip.del_veth.assert_called_once_with(
-                fip_ns.get_int_device_name(ri.router['id']))
-            self.mock_ip_dev.route.delete_gateway.assert_called_once_with(
-                str(fip_to_rtr.ip), table=dvr_fip_ns.FIP_RT_TBL)
-
-            self.assertEqual(ri.dist_fip_count, 0)
-            self.assertFalse(fip_ns.has_subscribers())
-
-            self.assertIsNone(fip_ns.agent_gateway_port)
-            self.assertTrue(fip_ns.destroyed)
-            self.mock_ip.netns.delete.assert_called_once_with(
-                fip_ns.get_name())
-            self.assertEqual(self.mock_driver.unplug.call_count, 1)
+            ri.remove_floating_ip.assert_called_once_with(self.mock_ip_dev,
+                                                          '19.4.4.2/32')
         else:
-            self.assertFalse(self.mock_driver.unplug.called)
+            self.assertFalse(ri.remove_floating_ip.called)
 
     def test_external_gateway_removed_ext_gw_port_and_fip(self):
         self._test_external_gateway_removed_ext_gw_port_and_fip(fip_ns=True)
@@ -2022,41 +1875,3 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
             if not skip(managed_flag):
                 assertFlag(managed_flag)('AdvManagedFlag on',
                     self.utils_replace_file.call_args[0][1])
-
-    def test__put_fips_in_error_state(self):
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        ri = mock.Mock()
-        ri.router.get.return_value = [{'id': mock.sentinel.id1},
-                                      {'id': mock.sentinel.id2}]
-
-        statuses = agent._put_fips_in_error_state(ri)
-
-        expected = [{mock.sentinel.id1: l3_constants.FLOATINGIP_STATUS_ERROR,
-                     mock.sentinel.id2: l3_constants.FLOATINGIP_STATUS_ERROR}]
-        self.assertNotEqual(expected, statuses)
-
-    def test__process_snat_dnat_for_fip(self):
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        agent.process_router_floating_ip_nat_rules = mock.Mock(
-            side_effect=Exception)
-
-        self.assertRaises(n_exc.FloatingIpSetupException,
-                          agent._process_snat_dnat_for_fip,
-                          mock.sentinel.ri)
-
-        agent.process_router_floating_ip_nat_rules.assert_called_with(
-            mock.sentinel.ri)
-
-    def test__configure_fip_addresses(self):
-        agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
-        agent.process_router_floating_ip_addresses = mock.Mock(
-            side_effect=Exception)
-
-        self.assertRaises(n_exc.FloatingIpSetupException,
-                          agent._configure_fip_addresses,
-                          mock.sentinel.ri,
-                          mock.sentinel.ex_gw_port)
-
-        agent.process_router_floating_ip_addresses.assert_called_with(
-            mock.sentinel.ri,
-            mock.sentinel.ex_gw_port)