]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
VLAN support for DVR
authorVivekanandan Narasimhan <vivekanandan.narasimhan@hp.com>
Mon, 13 Oct 2014 06:15:14 +0000 (23:15 -0700)
committerVivekanandan Narasimhan <vivekanandan.narasimhan@hp.com>
Tue, 27 Jan 2015 05:08:04 +0000 (21:08 -0800)
Brings in VLAN underlay support for
distributed virtual router. In line with
it brings in the ability to route packets
between VLAN and VXLAN (or) VLAN and GRE
networks via distributed virtual routers.

Implements: blueprint neutron-ovs-dvr-vlan
Closes-Bug: #1374995
Change-Id: If1eb9deef76a49bd0ae0d90ad5ad6a495d97a3b2

neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py
neutron/plugins/openvswitch/agent/ovs_neutron_agent.py
neutron/plugins/openvswitch/common/constants.py
neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py [new file with mode: 0644]
neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py

index 6e2bceb5eb3e5ee95d89cc27481a7e64b749fb8a..4eeeb04fd0fd180afd0d50b8c0f6c025474ffc6f 100644 (file)
@@ -22,9 +22,9 @@ from neutron.common import constants as n_const
 from neutron.common import utils as n_utils
 from neutron.i18n import _LE, _LI, _LW
 from neutron.openstack.common import log as logging
+from neutron.plugins.common import constants as p_const
 from neutron.plugins.openvswitch.common import constants
 
-
 LOG = logging.getLogger(__name__)
 
 
@@ -116,6 +116,7 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
     #   1.0 Initial version
 
     def __init__(self, context, plugin_rpc, integ_br, tun_br,
+                 bridge_mappings, phys_brs, int_ofports, phys_ofports,
                  patch_int_ofport=constants.OFPORT_INVALID,
                  patch_tun_ofport=constants.OFPORT_INVALID,
                  host=None, enable_tunneling=False,
@@ -125,11 +126,15 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
         self.host = host
         self.enable_tunneling = enable_tunneling
         self.enable_distributed_routing = enable_distributed_routing
+        self.bridge_mappings = bridge_mappings
+        self.phys_brs = phys_brs
+        self.int_ofports = int_ofports
+        self.phys_ofports = phys_ofports
         self.reset_ovs_parameters(integ_br, tun_br,
                                   patch_int_ofport, patch_tun_ofport)
         self.reset_dvr_parameters()
         self.dvr_mac_address = None
-        if self.enable_tunneling and self.enable_distributed_routing:
+        if self.enable_distributed_routing:
             self.get_dvr_mac_address()
 
     def reset_ovs_parameters(self, integ_br, tun_br,
@@ -187,11 +192,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                 self.dvr_mac_address = details['mac_address']
                 return
 
-    def setup_dvr_flows_on_integ_tun_br(self):
-        '''Setup up initial dvr flows into br-int and br-tun'''
-        if not (self.enable_tunneling and self.enable_distributed_routing):
-            return
-
+    def setup_dvr_flows_on_integ_br(self):
+        '''Setup up initial dvr flows into br-int'''
         if not self.in_distributed_mode():
             return
 
@@ -209,38 +211,31 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                              priority=1,
                              actions="drop")
 
+        self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC_VLAN,
+                             priority=1,
+                             actions="drop")
+
         # Insert 'normal' action as the default for Table LOCAL_SWITCHING
         self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
                              priority=1,
                              actions="normal")
 
-        dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context)
-        LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs)
-        for mac in dvr_macs:
-            if mac['mac_address'] == self.dvr_mac_address:
-                continue
-            # Table 0 (default) will now sort DVR traffic from other
-            # traffic depending on in_port
+        for physical_network in self.bridge_mappings:
             self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
                                  priority=2,
-                                 in_port=self.patch_tun_ofport,
-                                 dl_src=mac['mac_address'],
-                                 actions="resubmit(,%s)" %
-                                 constants.DVR_TO_SRC_MAC)
-            # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud
-            # are not learnt, as they may
-            # result in flow explosions
-            self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
-                                 priority=1,
-                                 dl_src=mac['mac_address'],
-                                 actions="output:%s" % self.patch_int_ofport)
+                                 in_port=self.int_ofports[physical_network],
+                                 actions="drop")
 
-            self.registered_dvr_macs.add(mac['mac_address'])
+    def setup_dvr_flows_on_tun_br(self):
+        '''Setup up initial dvr flows into br-tun'''
+        if not self.enable_tunneling or not self.in_distributed_mode():
+            return
 
         self.tun_br.add_flow(priority=1,
                              in_port=self.patch_int_ofport,
                              actions="resubmit(,%s)" %
                              constants.DVR_PROCESS)
+
         # table-miss should be sent to learning table
         self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
                              priority=0,
@@ -252,6 +247,77 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                              actions="resubmit(,%s)" %
                              constants.PATCH_LV_TO_TUN)
 
+    def setup_dvr_flows_on_phys_br(self):
+        '''Setup up initial dvr flows into br-phys'''
+        if not self.in_distributed_mode():
+            return
+
+        for physical_network in self.bridge_mappings:
+            self.phys_brs[physical_network].add_flow(priority=2,
+                in_port=self.phys_ofports[physical_network],
+                actions="resubmit(,%s)" %
+                constants.DVR_PROCESS_VLAN)
+            self.phys_brs[physical_network].add_flow(priority=1,
+                actions="resubmit(,%s)" %
+                constants.DVR_NOT_LEARN_VLAN)
+            self.phys_brs[physical_network].add_flow(
+                table=constants.DVR_PROCESS_VLAN,
+                priority=0,
+                actions="resubmit(,%s)" %
+                constants.LOCAL_VLAN_TRANSLATION)
+            self.phys_brs[physical_network].add_flow(
+                table=constants.LOCAL_VLAN_TRANSLATION,
+                priority=2,
+                in_port=self.phys_ofports[physical_network],
+                actions="drop")
+            self.phys_brs[physical_network].add_flow(
+                table=constants.DVR_NOT_LEARN_VLAN,
+                priority=1,
+                actions="NORMAL")
+
+    def setup_dvr_mac_flows_on_all_brs(self):
+        if not self.in_distributed_mode():
+            LOG.debug("Not in distributed mode, ignoring invocation "
+                      "of get_dvr_mac_address_list() ")
+            return
+        dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context)
+        LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs)
+        for mac in dvr_macs:
+            if mac['mac_address'] == self.dvr_mac_address:
+                continue
+            for physical_network in self.bridge_mappings:
+                self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
+                    priority=4,
+                    in_port=self.int_ofports[physical_network],
+                    dl_src=mac['mac_address'],
+                    actions="resubmit(,%s)" %
+                    constants.DVR_TO_SRC_MAC_VLAN)
+                self.phys_brs[physical_network].add_flow(
+                    table=constants.DVR_NOT_LEARN_VLAN,
+                    priority=2,
+                    dl_src=mac,
+                    actions="output:%s" %
+                    self.phys_ofports[physical_network])
+
+            if self.enable_tunneling:
+                # Table 0 (default) will now sort DVR traffic from other
+                # traffic depending on in_port
+                self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
+                                     priority=2,
+                                     in_port=self.patch_tun_ofport,
+                                     dl_src=mac['mac_address'],
+                                     actions="resubmit(,%s)" %
+                                     constants.DVR_TO_SRC_MAC)
+                # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud
+                # are not learnt, as they may
+                # result in flow explosions
+                self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
+                                 priority=1,
+                                 dl_src=mac,
+                                 actions="output:%s" %
+                                 self.patch_int_ofport)
+            self.registered_dvr_macs.add(mac['mac_address'])
+
     def dvr_mac_address_update(self, dvr_macs):
         if not self.dvr_mac_address:
             LOG.debug("Self mac unknown, ignoring this "
@@ -272,25 +338,48 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
         dvr_macs_removed = self.registered_dvr_macs - dvr_host_macs
 
         for oldmac in dvr_macs_removed:
-            self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
-                                     in_port=self.patch_tun_ofport,
-                                     dl_src=oldmac)
-            self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN,
-                                     dl_src=oldmac)
+            for physical_network in self.bridge_mappings:
+                self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
+                    in_port=self.int_ofports[physical_network],
+                    dl_src=oldmac)
+                self.phys_brs[physical_network].delete_flows(
+                    table=constants.DVR_NOT_LEARN_VLAN,
+                    dl_src=oldmac)
+            if self.enable_tunneling:
+                self.int_br.delete_flows(table=constants.LOCAL_SWITCHING,
+                                         in_port=self.patch_tun_ofport,
+                                         dl_src=oldmac)
+                self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN,
+                                         dl_src=oldmac)
             LOG.debug("Removed DVR MAC flow for %s", oldmac)
             self.registered_dvr_macs.remove(oldmac)
 
         for newmac in dvr_macs_added:
-            self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
-                                 priority=2,
-                                 in_port=self.patch_tun_ofport,
-                                 dl_src=newmac,
-                                 actions="resubmit(,%s)" %
-                                 constants.DVR_TO_SRC_MAC)
-            self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
-                                 priority=1,
-                                 dl_src=newmac,
-                                 actions="output:%s" % self.patch_int_ofport)
+            for physical_network in self.bridge_mappings:
+                self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
+                    priority=4,
+                    in_port=self.int_ofports[physical_network],
+                    dl_src=newmac,
+                    actions="resubmit(,%s)" %
+                    constants.DVR_TO_SRC_MAC_VLAN)
+                self.phys_brs[physical_network].add_flow(
+                    table=constants.DVR_NOT_LEARN_VLAN,
+                    priority=2,
+                    dl_src=newmac,
+                    actions="output:%s" %
+                    self.phys_ofports[physical_network])
+            if self.enable_tunneling:
+                self.int_br.add_flow(table=constants.LOCAL_SWITCHING,
+                                     priority=2,
+                                     in_port=self.patch_tun_ofport,
+                                     dl_src=newmac,
+                                     actions="resubmit(,%s)" %
+                                     constants.DVR_TO_SRC_MAC)
+                self.tun_br.add_flow(table=constants.DVR_NOT_LEARN,
+                                     priority=1,
+                                     dl_src=newmac,
+                                     actions="output:%s" %
+                                     self.patch_int_ofport)
             LOG.debug("Added DVR MAC flow for %s", newmac)
             self.registered_dvr_macs.add(newmac)
 
@@ -301,9 +390,6 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
         return device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE
 
     def process_tunneled_network(self, network_type, lvid, segmentation_id):
-        if not (self.enable_tunneling and self.enable_distributed_routing):
-            return
-
         if self.in_distributed_mode():
             table_id = constants.DVR_NOT_LEARN
         else:
@@ -315,8 +401,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                              "resubmit(,%s)" %
                              (lvid, table_id))
 
-    def _bind_distributed_router_interface_port(self, port, fixed_ips,
-                                                device_owner, local_vlan):
+    def _bind_distributed_router_interface_port(self, port, lvm,
+                                                fixed_ips, device_owner):
         # since router port must have only one fixed IP, directly
         # use fixed_ips[0]
         subnet_uuid = fixed_ips[0]['subnet_id']
@@ -346,8 +432,14 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
         # DVR takes over
         ldm.set_dvr_owned(True)
 
+        table_id = constants.DVR_TO_SRC_MAC
+        vlan_to_use = lvm.vlan
+        if lvm.network_type == p_const.TYPE_VLAN:
+            table_id = constants.DVR_TO_SRC_MAC_VLAN
+            vlan_to_use = lvm.segmentation_id
+
         subnet_info = ldm.get_subnet_info()
-        ip_subnet = subnet_info['cidr']
+        ip_version = subnet_info['ip_version']
         local_compute_ports = (
             self.plugin_rpc.get_ports_on_host_by_subnet(
                 self.context, self.host, subnet_uuid))
@@ -363,67 +455,83 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                 # ensure if a compute port is already on
                 # a different dvr routed subnet
                 # if yes, queue this subnet to that port
-                ovsport = self.local_ports[vif.vif_id]
-                ovsport.add_subnet(subnet_uuid)
+                comp_ovsport = self.local_ports[vif.vif_id]
+                comp_ovsport.add_subnet(subnet_uuid)
             else:
                 # the compute port is discovered first here that its on
                 # a dvr routed subnet queue this subnet to that port
-                ovsport = OVSPort(vif.vif_id, vif.ofport,
+                comp_ovsport = OVSPort(vif.vif_id, vif.ofport,
                                   vif.vif_mac, prt['device_owner'])
-
-                ovsport.add_subnet(subnet_uuid)
-                self.local_ports[vif.vif_id] = ovsport
-
+                comp_ovsport.add_subnet(subnet_uuid)
+                self.local_ports[vif.vif_id] = comp_ovsport
             # create rule for just this vm port
-            self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
+            self.int_br.add_flow(table=table_id,
                                  priority=4,
-                                 dl_vlan=local_vlan,
-                                 dl_dst=ovsport.get_mac(),
+                                 dl_vlan=vlan_to_use,
+                                 dl_dst=comp_ovsport.get_mac(),
                                  actions="strip_vlan,mod_dl_src:%s,"
                                  "output:%s" %
                                  (subnet_info['gateway_mac'],
-                                  ovsport.get_ofport()))
-
-        # create rule to forward broadcast/multicast frames from dvr
-        # router interface to appropriate local tenant ports
-        ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
-        if csnat_ofport != constants.OFPORT_INVALID:
-            ofports = str(csnat_ofport) + ',' + ofports
-        ip_version = subnet_info['ip_version']
-        if ofports:
-            args = self._get_flow_args_by_version(
-                ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet,
-                2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                    (subnet_info['gateway_mac'], ofports)))
-            self.int_br.add_flow(**args)
-
-        args = {'table': constants.DVR_PROCESS,
-                'priority': 3,
-                'dl_vlan': local_vlan,
-                'actions': "drop"}
-        if ip_version == 4:
-            args['proto'] = 'arp'
-            args['nw_dst'] = subnet_info['gateway_ip']
-        else:
-            args['proto'] = 'icmp6'
-            args['icmp_type'] = n_const.ICMPV6_TYPE_RA
-            args['dl_src'] = subnet_info['gateway_mac']
-
-        self.tun_br.add_flow(**args)
-        self.tun_br.add_flow(table=constants.DVR_PROCESS,
-                             priority=2,
-                             dl_vlan=local_vlan,
-                             dl_dst=port.vif_mac,
-                             actions="drop")
+                                  comp_ovsport.get_ofport()))
 
-        self.tun_br.add_flow(table=constants.DVR_PROCESS,
-                             priority=1,
-                             dl_vlan=local_vlan,
-                             dl_src=port.vif_mac,
-                             actions="mod_dl_src:%s,resubmit(,%s)" %
-                             (self.dvr_mac_address,
-                              constants.PATCH_LV_TO_TUN))
+        if lvm.network_type == p_const.TYPE_VLAN:
+            args = {'table': constants.DVR_PROCESS_VLAN,
+                    'priority': 3,
+                    'dl_vlan': lvm.vlan,
+                    'actions': "drop"}
+            if ip_version == 4:
+                args['proto'] = 'arp'
+                args['nw_dst'] = subnet_info['gateway_ip']
+            else:
+                args['proto'] = 'icmp6'
+                args['icmp_type'] = n_const.ICMPV6_TYPE_RA
+                args['dl_src'] = subnet_info['gateway_mac']
+            # TODO(vivek) remove the IPv6 related add_flow once SNAT is not
+            # used for IPv6 DVR.
+            self.phys_brs[lvm.physical_network].add_flow(**args)
+            self.phys_brs[lvm.physical_network].add_flow(
+                table=constants.DVR_PROCESS_VLAN,
+                priority=2,
+                dl_vlan=lvm.vlan,
+                dl_dst=port.vif_mac,
+                actions="drop")
+
+            self.phys_brs[lvm.physical_network].add_flow(
+                table=constants.DVR_PROCESS_VLAN,
+                priority=1,
+                dl_vlan=lvm.vlan,
+                dl_src=port.vif_mac,
+                actions="mod_dl_src:%s,resubmit(,%s)" %
+                (self.dvr_mac_address, constants.LOCAL_VLAN_TRANSLATION))
+
+        if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
+            args = {'table': constants.DVR_PROCESS,
+                    'priority': 3,
+                    'dl_vlan': lvm.vlan,
+                    'actions': "drop"}
+            if ip_version == 4:
+                args['proto'] = 'arp'
+                args['nw_dst'] = subnet_info['gateway_ip']
+            else:
+                args['proto'] = 'icmp6'
+                args['icmp_type'] = n_const.ICMPV6_TYPE_RA
+                args['dl_src'] = subnet_info['gateway_mac']
+            # TODO(vivek) remove the IPv6 related add_flow once SNAT is not
+            # used for IPv6 DVR.
+            self.tun_br.add_flow(**args)
+            self.tun_br.add_flow(table=constants.DVR_PROCESS,
+                                 priority=2,
+                                 dl_vlan=lvm.vlan,
+                                 dl_dst=port.vif_mac,
+                                 actions="drop")
 
+            self.tun_br.add_flow(table=constants.DVR_PROCESS,
+                                 priority=1,
+                                 dl_vlan=lvm.vlan,
+                                 dl_src=port.vif_mac,
+                                 actions="mod_dl_src:%s,resubmit(,%s)" %
+                                 (self.dvr_mac_address,
+                                  constants.PATCH_LV_TO_TUN))
         # the dvr router interface is itself a port, so capture it
         # queue this subnet to that port. A subnet appears only once as
         # a router interface on any given router
@@ -432,8 +540,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
         ovsport.add_subnet(subnet_uuid)
         self.local_ports[port.vif_id] = ovsport
 
-    def _bind_port_on_dvr_subnet(self, port, fixed_ips,
-                                 device_owner, local_vlan):
+    def _bind_port_on_dvr_subnet(self, port, lvm, fixed_ips,
+                                 device_owner):
         # Handle new compute port added use-case
         subnet_uuid = None
         for ips in fixed_ips:
@@ -452,8 +560,6 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
             # the integration bridge
             LOG.debug("DVR: Plumbing compute port %s", port.vif_id)
             subnet_info = ldm.get_subnet_info()
-            ip_subnet = subnet_info['cidr']
-            csnat_ofport = ldm.get_csnat_ofport()
             ldm.add_compute_ofport(port.vif_id, port.ofport)
             if port.vif_id in self.local_ports:
                 # ensure if a compute port is already on a different
@@ -466,31 +572,25 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                 # on a dvr routed subnet, queue this subnet to that port
                 ovsport = OVSPort(port.vif_id, port.ofport,
                                   port.vif_mac, device_owner)
-
                 ovsport.add_subnet(subnet_uuid)
                 self.local_ports[port.vif_id] = ovsport
+            table_id = constants.DVR_TO_SRC_MAC
+            vlan_to_use = lvm.vlan
+            if lvm.network_type == p_const.TYPE_VLAN:
+                table_id = constants.DVR_TO_SRC_MAC_VLAN
+                vlan_to_use = lvm.segmentation_id
             # create a rule for this vm port
-            self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
+            self.int_br.add_flow(table=table_id,
                                  priority=4,
-                                 dl_vlan=local_vlan,
+                                 dl_vlan=vlan_to_use,
                                  dl_dst=ovsport.get_mac(),
                                  actions="strip_vlan,mod_dl_src:%s,"
                                  "output:%s" %
                                  (subnet_info['gateway_mac'],
                                   ovsport.get_ofport()))
-            ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
 
-            if csnat_ofport != constants.OFPORT_INVALID:
-                ofports = str(csnat_ofport) + ',' + ofports
-            ip_version = subnet_info['ip_version']
-            args = self._get_flow_args_by_version(
-                ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet,
-                2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                    (subnet_info['gateway_mac'], ofports)))
-            self.int_br.add_flow(**args)
-
-    def _bind_centralized_snat_port_on_dvr_subnet(self, port, fixed_ips,
-                                                  device_owner, local_vlan):
+    def _bind_centralized_snat_port_on_dvr_subnet(self, port, lvm,
+                                                  fixed_ips, device_owner):
         if port.vif_id in self.local_ports:
             # throw an error if CSNAT port is already on a different
             # dvr routed subnet
@@ -523,220 +623,178 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
                           port.vif_mac, device_owner)
         ovsport.add_subnet(subnet_uuid)
         self.local_ports[port.vif_id] = ovsport
-
-        self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC,
+        table_id = constants.DVR_TO_SRC_MAC
+        vlan_to_use = lvm.vlan
+        if lvm.network_type == p_const.TYPE_VLAN:
+            table_id = constants.DVR_TO_SRC_MAC_VLAN
+            vlan_to_use = lvm.segmentation_id
+        self.int_br.add_flow(table=table_id,
                              priority=4,
-                             dl_vlan=local_vlan,
+                             dl_vlan=vlan_to_use,
                              dl_dst=ovsport.get_mac(),
                              actions="strip_vlan,mod_dl_src:%s,"
                              " output:%s" %
                              (subnet_info['gateway_mac'],
                               ovsport.get_ofport()))
-        ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
-        ofports = str(ldm.get_csnat_ofport()) + ',' + ofports
-        ip_subnet = subnet_info['cidr']
 
-        # TODO(xuhanp) remove the IPv6 related add_flow once SNAT is not
-        # used for IPv6 DVR.
-        ip_version = subnet_info['ip_version']
-        args = self._get_flow_args_by_version(
-            ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet,
-            2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                (subnet_info['gateway_mac'], ofports)))
-        self.int_br.add_flow(**args)
-
-    def bind_port_to_dvr(self, port, network_type, fixed_ips,
-                         device_owner, local_vlan_id):
+    def bind_port_to_dvr(self, port, local_vlan_map,
+                         fixed_ips, device_owner):
         if not self.in_distributed_mode():
             return
 
-        if network_type not in constants.TUNNEL_NETWORK_TYPES:
+        if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES
+                                               + [p_const.TYPE_VLAN]):
+            LOG.debug("DVR: Port %s is with network_type %s not supported"
+                      " for dvr plumbing" % (port.vif_id,
+                                             local_vlan_map.network_type))
             return
 
         if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE:
-            self._bind_distributed_router_interface_port(port, fixed_ips,
-                                                         device_owner,
-                                                         local_vlan_id)
+            self._bind_distributed_router_interface_port(port,
+                                                         local_vlan_map,
+                                                         fixed_ips,
+                                                         device_owner)
 
         if device_owner and n_utils.is_dvr_serviced(device_owner):
-            self._bind_port_on_dvr_subnet(port, fixed_ips,
-                                          device_owner,
-                                          local_vlan_id)
+            self._bind_port_on_dvr_subnet(port, local_vlan_map,
+                                          fixed_ips,
+                                          device_owner)
 
         if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT:
-            self._bind_centralized_snat_port_on_dvr_subnet(port, fixed_ips,
-                                                           device_owner,
-                                                           local_vlan_id)
-
-    def _unbind_distributed_router_interface_port(self, port, local_vlan):
+            self._bind_centralized_snat_port_on_dvr_subnet(port,
+                                                           local_vlan_map,
+                                                           fixed_ips,
+                                                           device_owner)
 
+    def _unbind_distributed_router_interface_port(self, port, lvm):
         ovsport = self.local_ports[port.vif_id]
-
         # removal of distributed router interface
         subnet_ids = ovsport.get_subnets()
         subnet_set = set(subnet_ids)
+        network_type = lvm.network_type
+        physical_network = lvm.physical_network
+        table_id = constants.DVR_TO_SRC_MAC
+        vlan_to_use = lvm.vlan
+        if network_type == p_const.TYPE_VLAN:
+            table_id = constants.DVR_TO_SRC_MAC_VLAN
+            vlan_to_use = lvm.segmentation_id
         # ensure we process for all the subnets laid on this removed port
         for sub_uuid in subnet_set:
             if sub_uuid not in self.local_dvr_map:
                 continue
-
             ldm = self.local_dvr_map[sub_uuid]
             subnet_info = ldm.get_subnet_info()
-            ip_subnet = subnet_info['cidr']
-
+            ip_version = subnet_info['ip_version']
             # DVR is no more owner
             ldm.set_dvr_owned(False)
-
             # remove all vm rules for this dvr subnet
             # clear of compute_ports altogether
             compute_ports = ldm.get_compute_ofports()
             for vif_id in compute_ports:
-                ovsport = self.local_ports[vif_id]
-                self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
-                                         dl_vlan=local_vlan,
-                                         dl_dst=ovsport.get_mac())
+                comp_port = self.local_ports[vif_id]
+                self.int_br.delete_flows(table=table_id,
+                                         dl_vlan=vlan_to_use,
+                                         dl_dst=comp_port.get_mac())
             ldm.remove_all_compute_ofports()
-            ip_version = subnet_info['ip_version']
-            if ldm.get_csnat_ofport() != -1:
-                # If there is a csnat port on this agent, preserve
-                # the local_dvr_map state
-                ofports = str(ldm.get_csnat_ofport())
-                args = self._get_flow_args_by_version(
-                    ip_version, constants.DVR_TO_SRC_MAC,
-                    local_vlan, ip_subnet,
-                    2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                        (subnet_info['gateway_mac'], ofports)))
-                self.int_br.add_flow(**args)
 
-            else:
-                # removed port is a distributed router interface
-                args = self._get_flow_args_by_version(
-                    ip_version, constants.DVR_TO_SRC_MAC,
-                    local_vlan, ip_subnet, None, None)
-                self.int_br.delete_flows(**args)
-                # remove subnet from local_dvr_map as no dvr (or) csnat
+            if ldm.get_csnat_ofport() == constants.OFPORT_INVALID:
+                # if there is no csnat port for this subnet, remove
+                # this subnet from local_dvr_map, as no dvr (or) csnat
                 # ports available on this agent anymore
                 self.local_dvr_map.pop(sub_uuid, None)
-
-            args = {'table': constants.DVR_PROCESS,
-                    'dl_vlan': local_vlan}
-            if ip_version == 4:
-                args['proto'] = 'arp'
-                args['nw_dst'] = subnet_info['gateway_ip']
-            else:
-                args['proto'] = 'icmp6'
-                args['icmp_type'] = n_const.ICMPV6_TYPE_RA
-                args['dl_src'] = subnet_info['gateway_mac']
-
-            self.tun_br.delete_flows(**args)
+            if network_type == p_const.TYPE_VLAN:
+                args = {'table': constants.DVR_PROCESS_VLAN,
+                        'dl_vlan': lvm.vlan}
+                if ip_version == 4:
+                    args['proto'] = 'arp'
+                    args['nw_dst'] = subnet_info['gateway_ip']
+                else:
+                    args['proto'] = 'icmp6'
+                    args['icmp_type'] = n_const.ICMPV6_TYPE_RA
+                    args['dl_src'] = subnet_info['gateway_mac']
+                self.phys_br[physical_network].delete_flows(**args)
+
+            if network_type in constants.TUNNEL_NETWORK_TYPES:
+                args = {'table': constants.DVR_PROCESS,
+                        'dl_vlan': lvm.vlan}
+                if ip_version == 4:
+                    args['proto'] = 'arp'
+                    args['nw_dst'] = subnet_info['gateway_ip']
+                else:
+                    args['proto'] = 'icmp6'
+                    args['icmp_type'] = n_const.ICMPV6_TYPE_RA
+                    args['dl_src'] = subnet_info['gateway_mac']
+                self.tun_br.delete_flows(**args)
             ovsport.remove_subnet(sub_uuid)
 
-        self.tun_br.delete_flows(table=constants.DVR_PROCESS,
-                                 dl_vlan=local_vlan,
-                                 dl_dst=port.vif_mac)
-
-        self.tun_br.delete_flows(table=constants.DVR_PROCESS,
-                                 dl_vlan=local_vlan,
-                                 dl_src=port.vif_mac)
+        if lvm.network_type == p_const.TYPE_VLAN:
+            self.phys_br[physical_network].delete_flows(
+                table=constants.DVR_PROCESS_VLAN,
+                dl_vlan=lvm.vlan,
+                dl_dst=port.vif_mac)
+            self.phys_br[physical_network].delete_flows(
+                table=constants.DVR_PROCESS_VLAN,
+                dl_vlan=lvm.vlan,
+                dl_src=port.vif_mac)
+
+        if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
+            self.tun_br.delete_flows(table=constants.DVR_PROCESS,
+                                     dl_vlan=lvm.vlan,
+                                     dl_dst=port.vif_mac)
+            self.tun_br.delete_flows(table=constants.DVR_PROCESS,
+                                     dl_vlan=lvm.vlan,
+                                     dl_src=port.vif_mac)
         # release port state
         self.local_ports.pop(port.vif_id, None)
 
-    def _unbind_port_on_dvr_subnet(self, port, local_vlan):
-
+    def _unbind_port_on_dvr_subnet(self, port, lvm):
         ovsport = self.local_ports[port.vif_id]
         # This confirms that this compute port being removed belonged
         # to a dvr hosted subnet.
-        # Accommodate this VM Port into the existing rule in
-        # the integration bridge
         LOG.debug("DVR: Removing plumbing for compute port %s", port)
         subnet_ids = ovsport.get_subnets()
         # ensure we process for all the subnets laid on this port
         for sub_uuid in subnet_ids:
             if sub_uuid not in self.local_dvr_map:
                 continue
-
             ldm = self.local_dvr_map[sub_uuid]
-            subnet_info = ldm.get_subnet_info()
             ldm.remove_compute_ofport(port.vif_id)
-            ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
-            ip_subnet = subnet_info['cidr']
-            ip_version = subnet_info['ip_version']
+            table_id = constants.DVR_TO_SRC_MAC
+            vlan_to_use = lvm.vlan
+            if lvm.network_type == p_const.TYPE_VLAN:
+                table_id = constants.DVR_TO_SRC_MAC_VLAN
+                vlan_to_use = lvm.segmentation_id
             # first remove this vm port rule
-            self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
-                                     dl_vlan=local_vlan,
+            self.int_br.delete_flows(table=table_id,
+                                     dl_vlan=vlan_to_use,
                                      dl_dst=ovsport.get_mac())
-            if ldm.get_csnat_ofport() != -1:
-                # If there is a csnat port on this agent, preserve
-                # the local_dvr_map state
-                ofports = str(ldm.get_csnat_ofport()) + ',' + ofports
-                args = self._get_flow_args_by_version(
-                    ip_version, constants.DVR_TO_SRC_MAC,
-                    local_vlan, ip_subnet,
-                    2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                        (subnet_info['gateway_mac'], ofports)))
-                self.int_br.add_flow(**args)
-            else:
-                if ofports:
-                    args = self._get_flow_args_by_version(
-                        ip_version, constants.DVR_TO_SRC_MAC,
-                        local_vlan, ip_subnet,
-                        2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                            (subnet_info['gateway_mac'], ofports)))
-                    self.int_br.add_flow(**args)
-                else:
-                    # remove the flow altogether, as no ports (both csnat/
-                    # compute) are available on this subnet in this
-                    # agent
-                    args = self._get_flow_args_by_version(
-                        ip_version, constants.DVR_TO_SRC_MAC,
-                        local_vlan, ip_subnet, None, None)
-                    self.int_br.delete_flows(**args)
-
         # release port state
         self.local_ports.pop(port.vif_id, None)
 
-    def _unbind_centralized_snat_port_on_dvr_subnet(self, port, local_vlan):
-
+    def _unbind_centralized_snat_port_on_dvr_subnet(self, port, lvm):
         ovsport = self.local_ports[port.vif_id]
         # This confirms that this compute port being removed belonged
         # to a dvr hosted subnet.
-        # Accommodate this VM Port into the existing rule in
-        # the integration bridge
         LOG.debug("DVR: Removing plumbing for csnat port %s", port)
         sub_uuid = list(ovsport.get_subnets())[0]
         # ensure we process for all the subnets laid on this port
         if sub_uuid not in self.local_dvr_map:
             return
         ldm = self.local_dvr_map[sub_uuid]
-        subnet_info = ldm.get_subnet_info()
-        ip_subnet = subnet_info['cidr']
         ldm.set_csnat_ofport(constants.OFPORT_INVALID)
+        table_id = constants.DVR_TO_SRC_MAC
+        vlan_to_use = lvm.vlan
+        if lvm.network_type == p_const.TYPE_VLAN:
+            table_id = constants.DVR_TO_SRC_MAC_VLAN
+            vlan_to_use = lvm.segmentation_id
         # then remove csnat port rule
-        self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC,
-                                 dl_vlan=local_vlan,
+        self.int_br.delete_flows(table=table_id,
+                                 dl_vlan=vlan_to_use,
                                  dl_dst=ovsport.get_mac())
-        ofports = ','.join(map(str, ldm.get_compute_ofports().values()))
-        ip_version = subnet_info['ip_version']
-        if ofports:
-            # TODO(xuhanp) remove the IPv6 related add_flow once SNAT is not
-            # used for IPv6 DVR.
-            args = self._get_flow_args_by_version(
-                ip_version, constants.DVR_TO_SRC_MAC,
-                local_vlan, ip_subnet,
-                2, ("strip_vlan,mod_dl_src:%s,output:%s" %
-                    (subnet_info['gateway_mac'], ofports)))
-            self.int_br.add_flow(**args)
-        else:
-            args = self._get_flow_args_by_version(
-                ip_version, constants.DVR_TO_SRC_MAC,
-                local_vlan, ip_subnet, None, None)
-            self.int_br.delete_flows(**args)
-
         if not ldm.is_dvr_owned():
             # if not owned by DVR (only used for csnat), remove this
             # subnet state altogether
             self.local_dvr_map.pop(sub_uuid, None)
-
         # release port state
         self.local_ports.pop(port.vif_id, None)
 
@@ -761,7 +819,7 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
             args['actions'] = actions
         return args
 
-    def unbind_port_from_dvr(self, vif_port, local_vlan_id):
+    def unbind_port_from_dvr(self, vif_port, local_vlan_map):
         if not self.in_distributed_mode():
             return
         # Handle port removed use-case
@@ -774,11 +832,12 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin):
 
         if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE:
             self._unbind_distributed_router_interface_port(vif_port,
-                                                           local_vlan_id)
+                                                           local_vlan_map)
 
         if device_owner and n_utils.is_dvr_serviced(device_owner):
-            self._unbind_port_on_dvr_subnet(vif_port, local_vlan_id)
+            self._unbind_port_on_dvr_subnet(vif_port,
+                                            local_vlan_map)
 
         if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT:
             self._unbind_centralized_snat_port_on_dvr_subnet(vif_port,
-                                                             local_vlan_id)
+                                                             local_vlan_map)
index 54c7061e498467e30ffdb40a5a29f7398cdad5b2..13dae7aaad8e3b7f17a1bb688e2141ea31aae2f5 100644 (file)
@@ -221,6 +221,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
             self.dvr_plugin_rpc,
             self.int_br,
             self.tun_br,
+            self.bridge_mappings,
+            self.phys_brs,
+            self.int_ofports,
+            self.phys_ofports,
             self.patch_int_ofport,
             self.patch_tun_ofport,
             cfg.CONF.host,
@@ -236,7 +240,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
         if self.enable_tunneling:
             self.setup_tunnel_br()
 
-        self.dvr_agent.setup_dvr_flows_on_integ_tun_br()
+        self.dvr_agent.setup_dvr_flows_on_integ_br()
+        self.dvr_agent.setup_dvr_flows_on_tun_br()
+        self.dvr_agent.setup_dvr_flows_on_phys_br()
+        self.dvr_agent.setup_dvr_mac_flows_on_all_brs()
 
         # Collect additional bridges to monitor
         self.ancillary_brs = self.setup_ancillary_bridges(integ_br, tun_br)
@@ -530,10 +537,18 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
             if physical_network in self.phys_brs:
                 # outbound
                 br = self.phys_brs[physical_network]
-                br.add_flow(priority=4,
+                if self.enable_distributed_routing:
+                    br.add_flow(table=constants.LOCAL_VLAN_TRANSLATION,
+                            priority=4,
                             in_port=self.phys_ofports[physical_network],
                             dl_vlan=lvid,
                             actions="mod_vlan_vid:%s,normal" % segmentation_id)
+                else:
+                    br.add_flow(priority=4,
+                            in_port=self.phys_ofports[physical_network],
+                            dl_vlan=lvid,
+                            actions="mod_vlan_vid:%s,normal" % segmentation_id)
+
                 # inbound
                 self.int_br.add_flow(priority=3,
                                      in_port=self.
@@ -637,9 +652,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
         lvm = self.local_vlan_map[net_uuid]
         lvm.vif_ports[port.vif_id] = port
 
-        self.dvr_agent.bind_port_to_dvr(port, network_type, fixed_ips,
-                                        device_owner,
-                                        local_vlan_id=lvm.vlan)
+        self.dvr_agent.bind_port_to_dvr(port, lvm,
+                                        fixed_ips,
+                                        device_owner)
 
         # Do not bind a port if it's already bound
         cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
@@ -670,8 +685,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
 
         if vif_id in lvm.vif_ports:
             vif_port = lvm.vif_ports[vif_id]
-            self.dvr_agent.unbind_port_from_dvr(vif_port,
-                                                local_vlan_id=lvm.vlan)
+            self.dvr_agent.unbind_port_from_dvr(vif_port, lvm)
         lvm.vif_ports.pop(vif_id, None)
 
         if not lvm.vif_ports:
index 98c122db7d38bdb8a13a8da9ce589ff5bae0e7ca..6d84628df3453aa1cfc5e364b04c9ea2392ca910 100644 (file)
@@ -37,6 +37,7 @@ TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN]
 # Various tables for DVR use of integration bridge flows
 LOCAL_SWITCHING = 0
 DVR_TO_SRC_MAC = 1
+DVR_TO_SRC_MAC_VLAN = 2
 
 # Various tables for tunneling flows
 DVR_PROCESS = 1
@@ -49,6 +50,11 @@ UCAST_TO_TUN = 20
 ARP_RESPONDER = 21
 FLOOD_TO_TUN = 22
 
+# Various tables for DVR use of physical bridge flows
+DVR_PROCESS_VLAN = 1
+LOCAL_VLAN_TRANSLATION = 2
+DVR_NOT_LEARN_VLAN = 3
+
 # Tables for integration bridge
 # Table 0 is used for forwarding.
 CANARY_TABLE = 23
diff --git a/neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py
new file mode 100644 (file)
index 0000000..dbef8aa
--- /dev/null
@@ -0,0 +1,815 @@
+# Copyright (c) 2012 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 contextlib
+
+import mock
+from oslo.config import cfg
+from oslo import messaging
+
+from neutron.agent.linux import utils
+from neutron.common import constants as n_const
+from neutron.plugins.common import constants as p_const
+from neutron.plugins.openvswitch.agent import ovs_neutron_agent
+from neutron.plugins.openvswitch.common import constants
+from neutron.tests import base
+
+
+NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi'
+OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0"
+
+FAKE_MAC = '00:11:22:33:44:55'
+FAKE_IP1 = '10.0.0.1'
+FAKE_IP2 = '10.0.0.2'
+
+
+class CreateAgentConfigMapDvr(base.BaseTestCase):
+
+    def test_create_agent_config_map_enable_distributed_routing(self):
+        self.addCleanup(cfg.CONF.reset)
+        # Verify setting only enable_tunneling will default tunnel_type to GRE
+        cfg.CONF.set_override('enable_distributed_routing', True,
+                              group='AGENT')
+        cfgmap = ovs_neutron_agent.create_agent_config_map(cfg.CONF)
+        self.assertEqual(cfgmap['enable_distributed_routing'], True)
+
+
+class TestOvsDvrNeutronAgent(base.BaseTestCase):
+
+    def setUp(self):
+        super(TestOvsDvrNeutronAgent, self).setUp()
+        notifier_p = mock.patch(NOTIFIER)
+        notifier_cls = notifier_p.start()
+        self.notifier = mock.Mock()
+        notifier_cls.return_value = self.notifier
+        cfg.CONF.set_default('firewall_driver',
+                             'neutron.agent.firewall.NoopFirewallDriver',
+                             group='SECURITYGROUP')
+        kwargs = ovs_neutron_agent.create_agent_config_map(cfg.CONF)
+
+        class MockFixedIntervalLoopingCall(object):
+            def __init__(self, f):
+                self.f = f
+
+            def start(self, interval=0):
+                self.f()
+
+        with contextlib.nested(
+            mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
+                       'OVSNeutronAgent.setup_integration_br',
+                       return_value=mock.Mock()),
+            mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
+                       'OVSNeutronAgent.setup_ancillary_bridges',
+                       return_value=[]),
+            mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                       'create'),
+            mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                       'set_secure_mode'),
+            mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                       'get_local_port_mac',
+                       return_value='00:00:00:00:00:01'),
+            mock.patch('neutron.agent.linux.utils.get_interface_mac',
+                       return_value='00:00:00:00:00:01'),
+            mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges'),
+            mock.patch('neutron.openstack.common.loopingcall.'
+                       'FixedIntervalLoopingCall',
+                       new=MockFixedIntervalLoopingCall)):
+            self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs)
+            # set back to true because initial report state will succeed due
+            # to mocked out RPC calls
+            self.agent.use_call = True
+            self.agent.tun_br = mock.Mock()
+        self.agent.sg_agent = mock.Mock()
+
+    def _setup_for_dvr_test(self, ofport=10):
+        self._port = mock.Mock()
+        self._port.ofport = ofport
+        self._port.vif_id = "1234-5678-90"
+        self._physical_network = 'physeth1'
+        self._old_local_vlan = None
+        self._segmentation_id = 2001
+        self.agent.enable_distributed_routing = True
+        self.agent.enable_tunneling = True
+        self.agent.patch_tun_ofport = 1
+        self.agent.patch_int_ofport = 2
+        self.agent.dvr_agent.local_ports = {}
+        self.agent.local_vlan_map = {}
+        self.agent.dvr_agent.enable_distributed_routing = True
+        self.agent.dvr_agent.enable_tunneling = True
+        self.agent.dvr_agent.patch_tun_ofport = 1
+        self.agent.dvr_agent.patch_int_ofport = 2
+        self.agent.dvr_agent.tun_br = mock.Mock()
+        self.agent.dvr_agent.phys_brs[self._physical_network] = mock.Mock()
+        self.agent.dvr_agent.bridge_mappings = {self._physical_network:
+                                                'br-eth1'}
+        self.agent.dvr_agent.int_ofports[self._physical_network] = 30
+        self.agent.dvr_agent.phys_ofports[self._physical_network] = 40
+        self.agent.dvr_agent.local_dvr_map = {}
+        self.agent.dvr_agent.registered_dvr_macs = set()
+        self.agent.dvr_agent.dvr_mac_address = 'aa:22:33:44:55:66'
+        self._net_uuid = 'my-net-uuid'
+        self._fixed_ips = [{'subnet_id': 'my-subnet-uuid',
+                            'ip_address': '1.1.1.1'}]
+        self._compute_port = mock.Mock()
+        self._compute_port.ofport = 20
+        self._compute_port.vif_id = "1234-5678-91"
+        self._compute_fixed_ips = [{'subnet_id': 'my-subnet-uuid',
+                                    'ip_address': '1.1.1.3'}]
+
+    def _test_port_bound_for_dvr_on_vlan_network(self, device_owner,
+                                                 ip_version=4):
+        self._setup_for_dvr_test()
+        if ip_version == 4:
+            gateway_ip = '1.1.1.1'
+            cidr = '1.1.1.0/24'
+        else:
+            gateway_ip = '2001:100::1'
+            cidr = '2001:100::0/64'
+        self._port.vif_mac = gateway_mac = 'aa:bb:cc:11:22:33'
+        self._compute_port.vif_mac = '77:88:99:00:11:22'
+        physical_network = self._physical_network
+        segmentation_id = self._segmentation_id
+        network_type = p_const.TYPE_VLAN
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=str(self._old_local_vlan)),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                                  'get_subnet_for_dvr',
+                                  return_value={
+                                      'gateway_ip': gateway_ip,
+                                      'cidr': cidr,
+                                      'ip_version': ip_version,
+                                      'gateway_mac': gateway_mac}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'),
+                mock.patch.object(
+                    self.agent.dvr_agent.phys_brs[physical_network],
+                    'add_flow'),
+                mock.patch.object(
+                    self.agent.dvr_agent.phys_brs[physical_network],
+                    'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn, add_flow_phys_fn,
+                  delete_flows_phys_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, network_type,
+                    physical_network, segmentation_id, self._fixed_ips,
+                    n_const.DEVICE_OWNER_DVR_INTERFACE, False)
+                lvm = self.agent.local_vlan_map[self._net_uuid]
+                phy_ofp = self.agent.dvr_agent.phys_ofports[physical_network]
+                int_ofp = self.agent.dvr_agent.int_ofports[physical_network]
+                expected_on_phys_br = [
+                    mock.call(table=constants.LOCAL_VLAN_TRANSLATION,
+                              priority=4,
+                              in_port=phy_ofp,
+                              dl_vlan=lvm.vlan,
+                              actions="mod_vlan_vid:%s,normal" %
+                              (lvm.segmentation_id)),
+                    mock.call(table=constants.DVR_PROCESS_VLAN,
+                              priority=2,
+                              dl_vlan=lvm.vlan,
+                              dl_dst=self._port.vif_mac,
+                              actions="drop"),
+                    mock.call(table=constants.DVR_PROCESS_VLAN,
+                              priority=1,
+                              dl_vlan=lvm.vlan,
+                              dl_src=self._port.vif_mac,
+                              actions="mod_dl_src:%s,resubmit(,%s)" %
+                              (self.agent.dvr_agent.dvr_mac_address,
+                               constants.LOCAL_VLAN_TRANSLATION))
+                            ]
+                if ip_version == 4:
+                    expected_on_phys_br.insert(1, mock.call(
+                        proto='arp',
+                        nw_dst=gateway_ip, actions='drop',
+                        priority=3, table=constants.DVR_PROCESS_VLAN,
+                        dl_vlan=lvm.vlan))
+                else:
+                    expected_on_phys_br.insert(1, mock.call(
+                        icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6',
+                        dl_src=self._port.vif_mac, actions='drop',
+                        priority=3, table=constants.DVR_PROCESS_VLAN,
+                        dl_vlan=lvm.vlan))
+                self.assertEqual(expected_on_phys_br,
+                                 add_flow_phys_fn.call_args_list)
+                self.agent.port_bound(self._compute_port, self._net_uuid,
+                                      network_type, physical_network,
+                                      segmentation_id,
+                                      self._compute_fixed_ips,
+                                      device_owner, False)
+                expected_on_int_br = [
+                    mock.call(priority=3,
+                              in_port=int_ofp,
+                              dl_vlan=lvm.segmentation_id,
+                              actions="mod_vlan_vid:%s,normal" % lvm.vlan),
+                    mock.call(table=constants.DVR_TO_SRC_MAC_VLAN,
+                              priority=4,
+                              dl_dst=self._compute_port.vif_mac,
+                              dl_vlan=lvm.segmentation_id,
+                              actions="strip_vlan,mod_dl_src:%s,"
+                              "output:%s" %
+                              (gateway_mac,
+                               self._compute_port.ofport))
+                                      ]
+                self.assertEqual(expected_on_int_br,
+                                 add_flow_int_fn.call_args_list)
+                expected_on_int_br = [
+                    mock.call(in_port=self._port.ofport),
+                    mock.call(in_port=self._compute_port.ofport)
+                                      ]
+                self.assertEqual(expected_on_int_br,
+                                 delete_flows_int_fn.call_args_list)
+                self.assertFalse(add_flow_tun_fn.called)
+                self.assertFalse(delete_flows_tun_fn.called)
+                self.assertFalse(delete_flows_phys_fn.called)
+
+    def _test_port_bound_for_dvr_on_vxlan_network(self, device_owner,
+                                                  ip_version=4):
+        self._setup_for_dvr_test()
+        if ip_version == 4:
+            gateway_ip = '1.1.1.1'
+            cidr = '1.1.1.0/24'
+        else:
+            gateway_ip = '2001:100::1'
+            cidr = '2001:100::0/64'
+        network_type = p_const.TYPE_VXLAN
+        self._port.vif_mac = gateway_mac = 'aa:bb:cc:11:22:33'
+        self._compute_port.vif_mac = '77:88:99:00:11:22'
+        physical_network = self._physical_network
+        segmentation_id = self._segmentation_id
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=self._old_local_vlan),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                                  'get_subnet_for_dvr',
+                                  return_value={
+                                      'gateway_ip': gateway_ip,
+                                      'cidr': cidr,
+                                      'ip_version': ip_version,
+                                      'gateway_mac': gateway_mac}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'),
+                mock.patch.object(
+                    self.agent.dvr_agent.phys_brs[physical_network],
+                    'add_flow'),
+                mock.patch.object(
+                    self.agent.dvr_agent.phys_brs[physical_network],
+                    'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn,
+                  add_flow_phys_fn, delete_flows_phys_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, network_type,
+                    physical_network, segmentation_id, self._fixed_ips,
+                    n_const.DEVICE_OWNER_DVR_INTERFACE, False)
+                lvm = self.agent.local_vlan_map[self._net_uuid]
+                expected_in_tun_br = [
+                    mock.call(
+                        table=constants.TUN_TABLE['vxlan'],
+                        priority=1, tun_id=lvm.segmentation_id,
+                        actions="mod_vlan_vid:%s,"
+                        "resubmit(,%s)" %
+                        (lvm.vlan, constants.DVR_NOT_LEARN)),
+                    mock.call(
+                        table=constants.DVR_PROCESS, priority=2,
+                        dl_vlan=lvm.vlan,
+                        dl_dst=self._port.vif_mac,
+                        actions='drop'),
+                    mock.call(
+                        table=constants.DVR_PROCESS, priority=1,
+                        dl_vlan=lvm.vlan,
+                        dl_src=self._port.vif_mac,
+                        actions="mod_dl_src:%s,resubmit(,%s)" % (
+                            self.agent.dvr_agent.dvr_mac_address,
+                            constants.PATCH_LV_TO_TUN))]
+                if ip_version == 4:
+                    expected_in_tun_br.insert(1, mock.call(
+                        proto='arp',
+                        nw_dst=gateway_ip, actions='drop',
+                        priority=3, table=constants.DVR_PROCESS,
+                        dl_vlan=lvm.vlan))
+                else:
+                    expected_in_tun_br.insert(1, mock.call(
+                        icmp_type=n_const.ICMPV6_TYPE_RA,
+                        proto='icmp6',
+                        dl_src=self._port.vif_mac,
+                        actions='drop',
+                        priority=3, table=constants.DVR_PROCESS,
+                        dl_vlan=lvm.vlan))
+                self.assertEqual(expected_in_tun_br,
+                                 add_flow_tun_fn.call_args_list)
+                self.agent.port_bound(self._compute_port, self._net_uuid,
+                                      network_type, physical_network,
+                                      segmentation_id,
+                                      self._compute_fixed_ips,
+                                      device_owner, False)
+                expected_in_int_br = [
+                    mock.call(table=constants.DVR_TO_SRC_MAC, priority=4,
+                        dl_dst=self._compute_port.vif_mac,
+                        dl_vlan=lvm.vlan,
+                        actions="strip_vlan,mod_dl_src:%s,"
+                        "output:%s" %
+                        (gateway_mac, self._compute_port.ofport))
+                                ]
+                self.assertEqual(expected_in_int_br,
+                                 add_flow_int_fn.call_args_list)
+                self.assertFalse(add_flow_phys_fn.called)
+                expected_in_int_br = [
+                    mock.call(in_port=self._port.ofport),
+                    mock.call(in_port=self._compute_port.ofport)
+                                      ]
+                self.assertEqual(expected_in_int_br,
+                                 delete_flows_int_fn.call_args_list)
+                self.assertFalse(add_flow_phys_fn.called)
+                self.assertFalse(delete_flows_tun_fn.called)
+                self.assertFalse(delete_flows_phys_fn.called)
+
+    def test_port_bound_for_dvr_with_compute_ports(self):
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner="compute:None")
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner="compute:None", ip_version=6)
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner="compute:None")
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner="compute:None", ip_version=6)
+
+    def test_port_bound_for_dvr_with_lbaas_vip_ports(self):
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER)
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6)
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER)
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6)
+
+    def test_port_bound_for_dvr_with_dhcp_ports(self):
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner=n_const.DEVICE_OWNER_DHCP)
+        self._test_port_bound_for_dvr_on_vlan_network(
+            device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6)
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner=n_const.DEVICE_OWNER_DHCP)
+        self._test_port_bound_for_dvr_on_vxlan_network(
+            device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6)
+
+    def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10):
+        self._setup_for_dvr_test()
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=self._old_local_vlan),
+                mock.patch.object(
+                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
+                    return_value={'gateway_ip': '1.1.1.1',
+                                  'cidr': '1.1.1.0/24',
+                                  'ip_version': 4,
+                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, 'vxlan',
+                    None, None, self._fixed_ips,
+                    n_const.DEVICE_OWNER_ROUTER_SNAT,
+                    False)
+                self.assertTrue(add_flow_int_fn.called)
+                self.assertTrue(delete_flows_int_fn.called)
+
+    def test_treat_devices_removed_for_dvr_interface(self, ofport=10):
+        self._test_treat_devices_removed_for_dvr_interface(ofport)
+        self._test_treat_devices_removed_for_dvr_interface(
+            ofport, ip_version=6)
+
+    def _test_treat_devices_removed_for_dvr_interface(self, ofport=10,
+                                                      ip_version=4):
+        self._setup_for_dvr_test()
+        if ip_version == 4:
+            gateway_ip = '1.1.1.1'
+            cidr = '1.1.1.0/24'
+        else:
+            gateway_ip = '2001:100::1'
+            cidr = '2001:100::0/64'
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=self._old_local_vlan),
+                mock.patch.object(
+                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
+                    return_value={'gateway_ip': gateway_ip,
+                                  'cidr': cidr,
+                                  'ip_version': ip_version,
+                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, 'vxlan',
+                    None, None, self._fixed_ips,
+                    n_const.DEVICE_OWNER_DVR_INTERFACE,
+                    False)
+                self.assertTrue(add_flow_tun_fn.called)
+                self.assertTrue(delete_flows_int_fn.called)
+
+        with contextlib.nested(
+            mock.patch.object(self.agent, 'reclaim_local_vlan'),
+            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
+                              return_value=None),
+            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+            mock.patch.object(self.agent.dvr_agent.tun_br,
+                              'delete_flows')) as (reclaim_vlan_fn,
+                                                   update_dev_down_fn,
+                                                   delete_flows_int_fn,
+                                                   delete_flows_tun_fn):
+                self.agent.treat_devices_removed([self._port.vif_id])
+                if ip_version == 4:
+                    expected = [mock.call(
+                        proto='arp',
+                        nw_dst=gateway_ip,
+                        table=constants.DVR_PROCESS,
+                        dl_vlan=(
+                            self.agent.local_vlan_map[self._net_uuid].vlan))]
+                else:
+                    expected = [mock.call(
+                        icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6',
+                        dl_src='aa:bb:cc:11:22:33',
+                        table=constants.DVR_PROCESS,
+                        dl_vlan=(
+                            self.agent.local_vlan_map[self._net_uuid].vlan))]
+                expected.extend([
+                    mock.call(
+                        table=constants.DVR_PROCESS,
+                        dl_dst=self._port.vif_mac,
+                        dl_vlan=(
+                            self.agent.local_vlan_map[self._net_uuid].vlan)),
+                    mock.call(
+                        table=constants.DVR_PROCESS,
+                        dl_vlan=(
+                            self.agent.local_vlan_map[self._net_uuid].vlan),
+                        dl_src=self._port.vif_mac)
+                ])
+                self.assertEqual(expected, delete_flows_tun_fn.call_args_list)
+
+    def _test_treat_devices_removed_for_dvr(self, device_owner, ip_version=4):
+        self._setup_for_dvr_test()
+        if ip_version == 4:
+            gateway_ip = '1.1.1.1'
+            cidr = '1.1.1.0/24'
+        else:
+            gateway_ip = '2001:100::1'
+            cidr = '2001:100::0/64'
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=self._old_local_vlan),
+                mock.patch.object(
+                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
+                    return_value={'gateway_ip': gateway_ip,
+                                  'cidr': cidr,
+                                  'ip_version': ip_version,
+                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, 'vxlan',
+                    None, None, self._fixed_ips,
+                    n_const.DEVICE_OWNER_DVR_INTERFACE,
+                    False)
+                self.agent.port_bound(self._compute_port,
+                                      self._net_uuid, 'vxlan',
+                                      None, None,
+                                      self._compute_fixed_ips,
+                                      device_owner, False)
+                self.assertTrue(add_flow_tun_fn.called)
+                self.assertTrue(add_flow_int_fn.called)
+                self.assertTrue(delete_flows_int_fn.called)
+
+        with contextlib.nested(
+            mock.patch.object(self.agent, 'reclaim_local_vlan'),
+            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
+                              return_value=None),
+            mock.patch.object(self.agent.dvr_agent.int_br,
+                              'delete_flows')) as (reclaim_vlan_fn,
+                                                   update_dev_down_fn,
+                                                   delete_flows_int_fn):
+                self.agent.treat_devices_removed([self._compute_port.vif_id])
+                expected = [
+                    mock.call(
+                        table=constants.DVR_TO_SRC_MAC,
+                        dl_dst=self._compute_port.vif_mac,
+                        dl_vlan=(
+                            self.agent.local_vlan_map[self._net_uuid].vlan))]
+                self.assertEqual(expected, delete_flows_int_fn.call_args_list)
+
+    def test_treat_devices_removed_for_dvr_with_compute_ports(self):
+        self._test_treat_devices_removed_for_dvr(
+            device_owner="compute:None")
+        self._test_treat_devices_removed_for_dvr(
+            device_owner="compute:None", ip_version=6)
+
+    def test_treat_devices_removed_for_dvr_with_lbaas_vip_ports(self):
+        self._test_treat_devices_removed_for_dvr(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER)
+        self._test_treat_devices_removed_for_dvr(
+            device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6)
+
+    def test_treat_devices_removed_for_dvr_with_dhcp_ports(self):
+        self._test_treat_devices_removed_for_dvr(
+            device_owner=n_const.DEVICE_OWNER_DHCP)
+        self._test_treat_devices_removed_for_dvr(
+            device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6)
+
+    def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10):
+        self._setup_for_dvr_test()
+        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                        'set_db_attribute',
+                        return_value=True):
+            with contextlib.nested(
+                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
+                           'db_get_val',
+                           return_value=self._old_local_vlan),
+                mock.patch.object(
+                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
+                    return_value={'gateway_ip': '1.1.1.1',
+                                  'cidr': '1.1.1.0/24',
+                                  'ip_version': 4,
+                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                    'get_ports_on_host_by_subnet',
+                    return_value=[]),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'get_vif_port_by_id',
+                                  return_value=self._port),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
+            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
+                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
+                  add_flow_tun_fn, delete_flows_tun_fn):
+                self.agent.port_bound(
+                    self._port, self._net_uuid, 'vxlan',
+                    None, None, self._fixed_ips,
+                    n_const.DEVICE_OWNER_ROUTER_SNAT,
+                    False)
+                self.assertTrue(add_flow_int_fn.called)
+                self.assertTrue(delete_flows_int_fn.called)
+
+        with contextlib.nested(
+            mock.patch.object(self.agent, 'reclaim_local_vlan'),
+            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
+                              return_value=None),
+            mock.patch.object(self.agent.dvr_agent.int_br,
+                              'delete_flows')) as (reclaim_vlan_fn,
+                                                   update_dev_down_fn,
+                                                   delete_flows_int_fn):
+                self.agent.treat_devices_removed([self._port.vif_id])
+                self.assertTrue(delete_flows_int_fn.called)
+
+    def test_setup_dvr_flows_on_int_br(self):
+        self._setup_for_dvr_test()
+        with contextlib.nested(
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'remove_all_flows'),
+                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+                mock.patch.object(
+                    self.agent.dvr_agent.plugin_rpc,
+                    'get_dvr_mac_address_list',
+                    return_value=[{'host': 'cn1',
+                                   'mac_address': 'aa:bb:cc:dd:ee:ff'},
+                                  {'host': 'cn2',
+                                   'mac_address': '11:22:33:44:55:66'}])) as \
+            (remove_flows_fn, add_int_flow_fn, add_tun_flow_fn,
+             get_mac_list_fn):
+            self.agent.dvr_agent.setup_dvr_flows_on_integ_br()
+            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
+            physical_networks = self.agent.dvr_agent.bridge_mappings.keys()
+            ioport = self.agent.dvr_agent.int_ofports[physical_networks[0]]
+            expected = [
+                    mock.call(table=constants.CANARY_TABLE,
+                              priority=0,
+                              actions="drop"),
+                    mock.call(table=constants.DVR_TO_SRC_MAC,
+                             priority=1,
+                             actions="drop"),
+                    mock.call(table=constants.DVR_TO_SRC_MAC_VLAN,
+                             priority=1,
+                             actions="drop"),
+                    mock.call(table=constants.LOCAL_SWITCHING,
+                             priority=1,
+                             actions="normal"),
+                    mock.call(
+                        table=constants.LOCAL_SWITCHING, priority=2,
+                        actions="drop",
+                        in_port=ioport)]
+            self.assertTrue(remove_flows_fn.called)
+            self.assertEqual(expected, add_int_flow_fn.call_args_list)
+            self.assertEqual(add_int_flow_fn.call_count, 5)
+
+    def test_get_dvr_mac_address(self):
+        self._setup_for_dvr_test()
+        self.agent.dvr_agent.dvr_mac_address = None
+        with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                               'get_dvr_mac_address_by_host',
+                               return_value={'host': 'cn1',
+                                  'mac_address': 'aa:22:33:44:55:66'}):
+            self.agent.dvr_agent.get_dvr_mac_address()
+            self.assertEqual('aa:22:33:44:55:66',
+                             self.agent.dvr_agent.dvr_mac_address)
+            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
+
+    def test_get_dvr_mac_address_exception(self):
+        self._setup_for_dvr_test()
+        self.agent.dvr_agent.dvr_mac_address = None
+        with contextlib.nested(
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                               'get_dvr_mac_address_by_host',
+                               side_effect=messaging.RemoteError),
+                mock.patch.object(self.agent.dvr_agent.int_br,
+                                  'add_flow')) as (gd_mac, add_int_flow_fn):
+
+            self.agent.dvr_agent.get_dvr_mac_address()
+            self.assertIsNone(self.agent.dvr_agent.dvr_mac_address)
+            self.assertFalse(self.agent.dvr_agent.in_distributed_mode())
+            self.assertEqual(add_int_flow_fn.call_count, 1)
+
+    def test_get_dvr_mac_address_retried(self):
+        valid_entry = {'host': 'cn1', 'mac_address': 'aa:22:33:44:55:66'}
+        raise_timeout = messaging.MessagingTimeout()
+        # Raise a timeout the first 2 times it calls get_dvr_mac_address()
+        self._setup_for_dvr_test()
+        self.agent.dvr_agent.dvr_mac_address = None
+        with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                               'get_dvr_mac_address_by_host',
+                               side_effect=(raise_timeout, raise_timeout,
+                                            valid_entry)):
+            self.agent.dvr_agent.get_dvr_mac_address()
+            self.assertEqual('aa:22:33:44:55:66',
+                             self.agent.dvr_agent.dvr_mac_address)
+            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
+            self.assertEqual(self.agent.dvr_agent.plugin_rpc.
+                             get_dvr_mac_address_by_host.call_count, 3)
+
+    def test_get_dvr_mac_address_retried_max(self):
+        raise_timeout = messaging.MessagingTimeout()
+        # Raise a timeout every time until we give up, currently 5 tries
+        self._setup_for_dvr_test()
+        self.agent.dvr_agent.dvr_mac_address = None
+        with contextlib.nested(
+                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
+                                 'get_dvr_mac_address_by_host',
+                                 side_effect=raise_timeout),
+                mock.patch.object(utils, "execute"),
+        ) as (rpc_mock, execute_mock):
+            self.agent.dvr_agent.get_dvr_mac_address()
+            self.assertIsNone(self.agent.dvr_agent.dvr_mac_address)
+            self.assertFalse(self.agent.dvr_agent.in_distributed_mode())
+            self.assertEqual(self.agent.dvr_agent.plugin_rpc.
+                             get_dvr_mac_address_by_host.call_count, 5)
+
+    def test_dvr_mac_address_update(self):
+        self._setup_for_dvr_test()
+        newhost = 'cn2'
+        newmac = 'aa:bb:cc:dd:ee:ff'
+        int_ofport = self.agent.dvr_agent.int_ofports['physeth1']
+        patch_int_ofport = self.agent.dvr_agent.patch_int_ofport
+        patch_tun_ofport = self.agent.dvr_agent.patch_tun_ofport
+        with contextlib.nested(
+            mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+            mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
+            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+            mock.patch.object(self.agent.dvr_agent.phys_brs['physeth1'],
+                              'add_flow')
+        ) as (add_flow_fn, add_flow_tn_fn, del_flows_fn, add_flow_phys_fn):
+            self.agent.dvr_agent.\
+                dvr_mac_address_update(
+                    dvr_macs=[{'host': newhost,
+                               'mac_address': newmac}])
+            expected = [
+                    mock.call(table=constants.LOCAL_SWITCHING,
+                              priority=4,
+                              in_port=int_ofport,
+                              dl_src=newmac,
+                              actions="resubmit(,%s)" %
+                              constants.DVR_TO_SRC_MAC_VLAN),
+                    mock.call(table=constants.LOCAL_SWITCHING,
+                              priority=2,
+                              in_port=patch_tun_ofport,
+                              dl_src=newmac,
+                              actions="resubmit(,%s)" %
+                              constants.DVR_TO_SRC_MAC)]
+            self.assertEqual(expected, add_flow_fn.call_args_list)
+            add_flow_phys_fn.assert_called_with(
+                    table=constants.DVR_NOT_LEARN_VLAN,
+                    priority=2,
+                    dl_src=newmac,
+                    actions="output:%s" %
+                    self.agent.dvr_agent.phys_ofports['physeth1'])
+            add_flow_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN,
+                                              priority=1,
+                                              dl_src=newmac,
+                                              actions="output:%s"
+                                              % patch_int_ofport)
+            self.assertFalse(del_flows_fn.called)
+        with contextlib.nested(
+            mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
+            mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'),
+            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
+            mock.patch.object(self.agent.dvr_agent.phys_brs['physeth1'],
+                              'delete_flows'),
+        ) as (add_flow_fn, del_flows_tn_fn, del_flows_fn, del_flows_phys_fn):
+            self.agent.dvr_agent.dvr_mac_address_update(dvr_macs=[])
+            ioport = self.agent.dvr_agent.int_ofports['physeth1']
+            expected = [
+                 mock.call(table=constants.LOCAL_SWITCHING,
+                           in_port=ioport,
+                           dl_src=newmac),
+                 mock.call(table=constants.LOCAL_SWITCHING,
+                           in_port=patch_tun_ofport,
+                           dl_src=newmac)]
+            self.assertEqual(expected, del_flows_fn.call_args_list)
+            del_flows_phys_fn.asert_called_with(
+                    table=constants.DVR_NOT_LEARN_VLAN,
+                    dl_src=newmac)
+            del_flows_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN,
+                                               dl_src=newmac)
+            self.assertFalse(add_flow_fn.called)
index baa71b1a6c44ee813fea0959c3ac498d0f9221e9..dad61670961d8a4c5d0fd86ba6f18213a3c985d5 100644 (file)
@@ -19,7 +19,6 @@ import time
 import mock
 import netaddr
 from oslo.config import cfg
-from oslo import messaging
 import testtools
 
 from neutron.agent.linux import async_process
@@ -168,34 +167,6 @@ class TestOvsNeutronAgent(base.BaseTestCase):
             self.assertFalse(set_ovs_db_func.called)
             self.assertFalse(delete_flows_func.called)
 
-    def _setup_for_dvr_test(self, ofport=10):
-        self._port = mock.Mock()
-        self._port.ofport = ofport
-        self._port.vif_id = "1234-5678-90"
-        self.agent.enable_distributed_routing = True
-        self.agent.enable_tunneling = True
-        self.agent.patch_tun_ofport = 1
-        self.agent.patch_int_ofport = 2
-        self.agent.dvr_agent.local_ports = {}
-        self.agent.local_vlan_map = {}
-        self.agent.dvr_agent.enable_distributed_routing = True
-        self.agent.dvr_agent.enable_tunneling = True
-        self.agent.dvr_agent.patch_tun_ofport = 1
-        self.agent.dvr_agent.patch_int_ofport = 2
-        self.agent.dvr_agent.tun_br = mock.Mock()
-        self.agent.dvr_agent.local_dvr_map = {}
-        self.agent.dvr_agent.registered_dvr_macs = set()
-        self.agent.dvr_agent.dvr_mac_address = 'aa:22:33:44:55:66'
-        self._net_uuid = 'my-net-uuid'
-        self._fixed_ips = [{'subnet_id': 'my-subnet-uuid',
-                            'ip_address': '1.1.1.1'}]
-        self._compute_port = mock.Mock()
-        self._compute_port.ofport = 20
-        self._compute_port.vif_id = "1234-5678-91"
-        self._old_local_vlan = None
-        self._compute_fixed_ips = [{'subnet_id': 'my-subnet-uuid',
-                                    'ip_address': '1.1.1.3'}]
-
     def test_port_bound_deletes_flows_for_valid_ofport(self):
         self._mock_port_bound(ofport=1, new_local_vlan=1)
 
@@ -205,541 +176,6 @@ class TestOvsNeutronAgent(base.BaseTestCase):
     def test_port_bound_does_not_rewire_if_already_bound(self):
         self._mock_port_bound(ofport=-1, new_local_vlan=1, old_local_vlan=1)
 
-    def test_port_bound_for_dvr_interface(self, ofport=10):
-        self._setup_for_dvr_test()
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
-                    return_value={'gateway_ip': '1.1.1.1',
-                                  'cidr': '1.1.1.0/24',
-                                  'ip_version': 4,
-                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_DVR_INTERFACE,
-                    False)
-                self.assertTrue(add_flow_tun_fn.called)
-                self.assertTrue(delete_flows_int_fn.called)
-
-    def _test_port_bound_for_dvr(self, device_owner, ip_version=4):
-        self._setup_for_dvr_test()
-        if ip_version == 4:
-            gateway_ip = '1.1.1.1'
-            cidr = '1.1.1.0/24'
-        else:
-            gateway_ip = '2001:100::1'
-            cidr = '2001:100::0/64'
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                                  'get_subnet_for_dvr',
-                                  return_value={
-                                      'gateway_ip': gateway_ip,
-                                      'cidr': cidr,
-                                      'ip_version': ip_version,
-                                      'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_DVR_INTERFACE,
-                    False)
-                expected = [
-                    mock.call(
-                        table=constants.TUN_TABLE['vxlan'],
-                        priority=1, tun_id=None,
-                        actions="mod_vlan_vid:%s,"
-                        "resubmit(,%s)" %
-                        (self.agent.local_vlan_map[self._net_uuid].vlan,
-                         constants.DVR_NOT_LEARN)),
-                    mock.call(
-                        table=constants.DVR_PROCESS, priority=2,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        dl_dst=self._port.vif_mac,
-                        actions='drop'),
-                    mock.call(
-                        table=constants.DVR_PROCESS, priority=1,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        dl_src=self._port.vif_mac,
-                        actions="mod_dl_src:%s,resubmit(,%s)" % (
-                            self.agent.dvr_agent.dvr_mac_address,
-                            constants.PATCH_LV_TO_TUN))]
-                if ip_version == 4:
-                    expected.insert(1, mock.call(
-                        proto='arp',
-                        nw_dst=gateway_ip, actions='drop',
-                        priority=3, table=constants.DVR_PROCESS,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan)))
-                else:
-                    expected.insert(1, mock.call(
-                        icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6',
-                        dl_src='aa:bb:cc:11:22:33', actions='drop',
-                        priority=3, table=constants.DVR_PROCESS,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan)))
-
-                self.assertEqual(expected, add_flow_tun_fn.call_args_list)
-                self.agent.port_bound(self._compute_port, self._net_uuid,
-                                      'vxlan', None, None,
-                                      self._compute_fixed_ips,
-                                      device_owner, False)
-                expected = [
-                    mock.call(table=constants.DVR_TO_SRC_MAC, priority=4,
-                        dl_dst=self._compute_port.vif_mac,
-                        dl_vlan=self.agent.local_vlan_map[self._net_uuid].vlan,
-                        actions="strip_vlan,mod_dl_src:%s,"
-                        "output:%s" %
-                        ('aa:bb:cc:11:22:33', self._compute_port.ofport))
-                ]
-                if ip_version == 4:
-                    expected.append(mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        priority=2, proto='ip',
-                        nw_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        actions="strip_vlan,mod_dl_src:%s,"
-                        "output:%s" %
-                        ('aa:bb:cc:11:22:33', self._compute_port.ofport)))
-                else:
-                    expected.append(mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        priority=2, proto='ipv6',
-                        ipv6_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        actions="strip_vlan,mod_dl_src:%s,"
-                        "output:%s" %
-                        ('aa:bb:cc:11:22:33', self._compute_port.ofport)))
-                self.assertEqual(expected, add_flow_int_fn.call_args_list)
-                self.assertTrue(delete_flows_int_fn.called)
-
-    def test_port_bound_for_dvr_with_compute_ports(self):
-        self._test_port_bound_for_dvr(
-            device_owner="compute:None")
-        self._test_port_bound_for_dvr(
-            device_owner="compute:None", ip_version=6)
-
-    def test_port_bound_for_dvr_with_lbaas_vip_ports(self):
-        self._test_port_bound_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_LOADBALANCER)
-        self._test_port_bound_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6)
-
-    def test_port_bound_for_dvr_with_dhcp_ports(self):
-        self._test_port_bound_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_DHCP)
-        self._test_port_bound_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6)
-
-    def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10):
-        self._setup_for_dvr_test()
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
-                    return_value={'gateway_ip': '1.1.1.1',
-                                  'cidr': '1.1.1.0/24',
-                                  'ip_version': 4,
-                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_ROUTER_SNAT,
-                    False)
-                self.assertTrue(add_flow_int_fn.called)
-                self.assertTrue(delete_flows_int_fn.called)
-
-    def test_treat_devices_removed_for_dvr_interface(self, ofport=10):
-        self._test_treat_devices_removed_for_dvr_interface(ofport)
-        self._test_treat_devices_removed_for_dvr_interface(
-            ofport, ip_version=6)
-
-    def _test_treat_devices_removed_for_dvr_interface(self, ofport=10,
-                                                      ip_version=4):
-        self._setup_for_dvr_test()
-        if ip_version == 4:
-            gateway_ip = '1.1.1.1'
-            cidr = '1.1.1.0/24'
-        else:
-            gateway_ip = '2001:100::1'
-            cidr = '2001:100::0/64'
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
-                    return_value={'gateway_ip': gateway_ip,
-                                  'cidr': cidr,
-                                  'ip_version': ip_version,
-                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_DVR_INTERFACE,
-                    False)
-                self.assertTrue(add_flow_tun_fn.called)
-                self.assertTrue(delete_flows_int_fn.called)
-
-        with contextlib.nested(
-            mock.patch.object(self.agent, 'reclaim_local_vlan'),
-            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
-                              return_value=None),
-            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-            mock.patch.object(self.agent.dvr_agent.tun_br,
-                              'delete_flows')) as (reclaim_vlan_fn,
-                                                   update_dev_down_fn,
-                                                   delete_flows_int_fn,
-                                                   delete_flows_tun_fn):
-                self.agent.treat_devices_removed([self._port.vif_id])
-                if ip_version == 4:
-                    expected = [mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        nw_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        proto='ip')]
-                else:
-                    expected = [mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        ipv6_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        proto='ipv6')]
-                self.assertEqual(expected,
-                                 delete_flows_int_fn.call_args_list)
-                if ip_version == 4:
-                    expected = [mock.call(
-                        proto='arp',
-                        nw_dst=gateway_ip,
-                        table=constants.DVR_PROCESS,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan))]
-                else:
-                    expected = [mock.call(
-                        icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6',
-                        dl_src='aa:bb:cc:11:22:33',
-                        table=constants.DVR_PROCESS,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan))]
-                expected.extend([
-                    mock.call(
-                        table=constants.DVR_PROCESS,
-                        dl_dst=self._port.vif_mac,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan)),
-                    mock.call(
-                        table=constants.DVR_PROCESS,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan),
-                        dl_src=self._port.vif_mac)
-                ])
-                self.assertEqual(expected, delete_flows_tun_fn.call_args_list)
-
-    def _test_treat_devices_removed_for_dvr(self, device_owner, ip_version=4):
-        self._setup_for_dvr_test()
-        if ip_version == 4:
-            gateway_ip = '1.1.1.1'
-            cidr = '1.1.1.0/24'
-        else:
-            gateway_ip = '2001:100::1'
-            cidr = '2001:100::0/64'
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
-                    return_value={'gateway_ip': gateway_ip,
-                                  'cidr': cidr,
-                                  'ip_version': ip_version,
-                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_DVR_INTERFACE,
-                    False)
-                self.agent.port_bound(self._compute_port,
-                                      self._net_uuid, 'vxlan',
-                                      None, None,
-                                      self._compute_fixed_ips,
-                                      device_owner, False)
-                self.assertTrue(add_flow_tun_fn.called)
-                self.assertTrue(add_flow_int_fn.called)
-                self.assertTrue(delete_flows_int_fn.called)
-
-        with contextlib.nested(
-            mock.patch.object(self.agent, 'reclaim_local_vlan'),
-            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
-                              return_value=None),
-            mock.patch.object(self.agent.dvr_agent.int_br,
-                              'delete_flows')) as (reclaim_vlan_fn,
-                                                   update_dev_down_fn,
-                                                   delete_flows_int_fn):
-                self.agent.treat_devices_removed([self._compute_port.vif_id])
-                expected = [
-                    mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        dl_dst=self._compute_port.vif_mac,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan))]
-                if ip_version == 4:
-                    expected.append(mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        proto='ip', nw_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan))
-                    )
-                else:
-                    expected.append(mock.call(
-                        table=constants.DVR_TO_SRC_MAC,
-                        proto='ipv6', ipv6_dst=cidr,
-                        dl_vlan=(
-                            self.agent.local_vlan_map[self._net_uuid].vlan))
-                    )
-                self.assertEqual(expected, delete_flows_int_fn.call_args_list)
-
-    def test_treat_devices_removed_for_dvr_with_compute_ports(self):
-        self._test_treat_devices_removed_for_dvr(
-            device_owner="compute:None")
-        self._test_treat_devices_removed_for_dvr(
-            device_owner="compute:None", ip_version=6)
-
-    def test_treat_devices_removed_for_dvr_with_lbaas_vip_ports(self):
-        self._test_treat_devices_removed_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_LOADBALANCER)
-        self._test_treat_devices_removed_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6)
-
-    def test_treat_devices_removed_for_dvr_with_dhcp_ports(self):
-        self._test_treat_devices_removed_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_DHCP)
-        self._test_treat_devices_removed_for_dvr(
-            device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6)
-
-    def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10):
-        self._setup_for_dvr_test()
-        with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                        'set_db_attribute',
-                        return_value=True):
-            with contextlib.nested(
-                mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
-                           'db_get_val',
-                           return_value=self._old_local_vlan),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr',
-                    return_value={'gateway_ip': '1.1.1.1',
-                                  'cidr': '1.1.1.0/24',
-                                  'ip_version': 4,
-                                  'gateway_mac': 'aa:bb:cc:11:22:33'}),
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                    'get_ports_on_host_by_subnet',
-                    return_value=[]),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'get_vif_port_by_id',
-                                  return_value=self._port),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-            ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn,
-                  get_vif_fn, add_flow_int_fn, delete_flows_int_fn,
-                  add_flow_tun_fn, delete_flows_tun_fn):
-                self.agent.port_bound(
-                    self._port, self._net_uuid, 'vxlan',
-                    None, None, self._fixed_ips,
-                    n_const.DEVICE_OWNER_ROUTER_SNAT,
-                    False)
-                self.assertTrue(add_flow_int_fn.called)
-                self.assertTrue(delete_flows_int_fn.called)
-
-        with contextlib.nested(
-            mock.patch.object(self.agent, 'reclaim_local_vlan'),
-            mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
-                              return_value=None),
-            mock.patch.object(self.agent.dvr_agent.int_br,
-                              'delete_flows')) as (reclaim_vlan_fn,
-                                                   update_dev_down_fn,
-                                                   delete_flows_int_fn):
-                self.agent.treat_devices_removed([self._port.vif_id])
-                self.assertTrue(delete_flows_int_fn.called)
-
-    def test_setup_dvr_flows_on_int_br(self):
-        self._setup_for_dvr_test()
-        with contextlib.nested(
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'remove_all_flows'),
-                mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-                mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-                mock.patch.object(
-                    self.agent.dvr_agent.plugin_rpc,
-                    'get_dvr_mac_address_list',
-                    return_value=[{'host': 'cn1',
-                                   'mac_address': 'aa:bb:cc:dd:ee:ff'},
-                                  {'host': 'cn2',
-                                   'mac_address': '11:22:33:44:55:66'}])) as \
-            (remove_flows_fn, add_int_flow_fn, add_tun_flow_fn,
-             get_mac_list_fn):
-            self.agent.dvr_agent.setup_dvr_flows_on_integ_tun_br()
-            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
-            self.assertTrue(remove_flows_fn.called)
-            self.assertEqual(add_int_flow_fn.call_count, 5)
-            self.assertEqual(add_tun_flow_fn.call_count, 5)
-
-    def test_get_dvr_mac_address(self):
-        self._setup_for_dvr_test()
-        self.agent.dvr_agent.dvr_mac_address = None
-        with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                               'get_dvr_mac_address_by_host',
-                               return_value={'host': 'cn1',
-                                  'mac_address': 'aa:22:33:44:55:66'}):
-
-            self.agent.dvr_agent.get_dvr_mac_address()
-            self.assertEqual('aa:22:33:44:55:66',
-                             self.agent.dvr_agent.dvr_mac_address)
-            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
-
-    def test_get_dvr_mac_address_exception(self):
-        self._setup_for_dvr_test()
-        self.agent.dvr_agent.dvr_mac_address = None
-        with contextlib.nested(
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                               'get_dvr_mac_address_by_host',
-                               side_effect=messaging.RemoteError),
-                mock.patch.object(self.agent.dvr_agent.int_br,
-                                  'add_flow')) as (gd_mac, add_int_flow_fn):
-
-            self.agent.dvr_agent.get_dvr_mac_address()
-            self.assertIsNone(self.agent.dvr_agent.dvr_mac_address)
-            self.assertFalse(self.agent.dvr_agent.in_distributed_mode())
-            self.assertEqual(add_int_flow_fn.call_count, 1)
-
-    def test_get_dvr_mac_address_retried(self):
-        valid_entry = {'host': 'cn1', 'mac_address': 'aa:22:33:44:55:66'}
-        raise_timeout = messaging.MessagingTimeout()
-        # Raise a timeout the first 2 times it calls get_dvr_mac_address()
-        self._setup_for_dvr_test()
-        self.agent.dvr_agent.dvr_mac_address = None
-        with mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                               'get_dvr_mac_address_by_host',
-                               side_effect=(raise_timeout, raise_timeout,
-                                            valid_entry)):
-
-            self.agent.dvr_agent.get_dvr_mac_address()
-            self.assertEqual('aa:22:33:44:55:66',
-                             self.agent.dvr_agent.dvr_mac_address)
-            self.assertTrue(self.agent.dvr_agent.in_distributed_mode())
-            self.assertEqual(self.agent.dvr_agent.plugin_rpc.
-                             get_dvr_mac_address_by_host.call_count, 3)
-
-    def test_get_dvr_mac_address_retried_max(self):
-        raise_timeout = messaging.MessagingTimeout()
-        # Raise a timeout every time until we give up, currently 5 tries
-        self._setup_for_dvr_test()
-        self.agent.dvr_agent.dvr_mac_address = None
-        with contextlib.nested(
-                mock.patch.object(self.agent.dvr_agent.plugin_rpc,
-                                 'get_dvr_mac_address_by_host',
-                                 side_effect=raise_timeout),
-                mock.patch.object(utils, "execute"),
-        ) as (rpc_mock, execute_mock):
-            self.agent.dvr_agent.get_dvr_mac_address()
-            self.assertIsNone(self.agent.dvr_agent.dvr_mac_address)
-            self.assertFalse(self.agent.dvr_agent.in_distributed_mode())
-            self.assertEqual(self.agent.dvr_agent.plugin_rpc.
-                             get_dvr_mac_address_by_host.call_count, 5)
-
     def _test_port_dead(self, cur_tag=None):
         port = mock.Mock()
         port.ofport = 1
@@ -1447,35 +883,6 @@ class TestOvsNeutronAgent(base.BaseTestCase):
             self.agent.reclaim_local_vlan('net2')
             del_port_fn.assert_called_once_with('gre-02020202')
 
-    def test_dvr_mac_address_update(self):
-        self._setup_for_dvr_test()
-        with contextlib.nested(
-            mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-            mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'),
-            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'),
-            #mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows')
-        ) as (add_flow_fn, add_flow_tn_fn, del_flows_fn):
-            self.agent.dvr_agent.\
-                dvr_mac_address_update(
-                    dvr_macs=[{'host': 'cn2',
-                               'mac_address': 'aa:bb:cc:dd:ee:ff'}])
-            add_flow_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN,
-                                              priority=1,
-                                              dl_src='aa:bb:cc:dd:ee:ff',
-                                              actions="output:%s"
-                                              % self.agent.patch_int_ofport
-                                              )
-            self.assertFalse(del_flows_fn.called)
-        with contextlib.nested(
-            mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'),
-            mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'),
-            mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows')
-        ) as (add_flow_fn, del_flows_tn_fn, del_flows_fn):
-            self.agent.dvr_agent.dvr_mac_address_update(dvr_macs=[])
-            del_flows_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN,
-                                               dl_src='aa:bb:cc:dd:ee:ff')
-            self.assertFalse(add_flow_fn.called)
-
     def test_daemon_loop_uses_polling_manager(self):
         with mock.patch(
             'neutron.agent.linux.polling.get_polling_manager') as mock_get_pm: