Merge "Make DVR router support FLAT network for ovs-agent"

This commit is contained in:
Zuul 2020-07-04 05:29:29 +00:00 committed by Gerrit Code Review
commit 792f4efce9
13 changed files with 299 additions and 138 deletions

View File

@ -771,32 +771,47 @@ class OVSFirewallDriver(firewall.FirewallDriver):
return {id_: port.neutron_port_dict return {id_: port.neutron_port_dict
for id_, port in self.sg_port_map.ports.items()} for id_, port in self.sg_port_map.ports.items()}
def install_vlan_direct_flow(self, mac, segment_id, ofport, local_vlan): def install_physical_direct_flow(self, mac, segment_id,
# If the port segment_id is not None/0, the ofport, local_vlan, network_type):
# port's network type must be VLAN type. actions = ('set_field:{:d}->reg{:d},'
if segment_id: 'set_field:{:d}->reg{:d},').format(
ofport,
ovsfw_consts.REG_PORT,
# This always needs the local vlan.
local_vlan,
ovsfw_consts.REG_NET)
if network_type == lib_const.TYPE_VLAN:
actions += 'strip_vlan,resubmit(,{:d})'.format(
ovs_consts.BASE_INGRESS_TABLE)
self._add_flow( self._add_flow(
table=ovs_consts.TRANSIENT_TABLE, table=ovs_consts.TRANSIENT_TABLE,
priority=90, priority=90,
dl_dst=mac, dl_dst=mac,
dl_vlan='0x%x' % segment_id, dl_vlan='0x%x' % segment_id,
actions='set_field:{:d}->reg{:d},' actions=actions)
'set_field:{:d}->reg{:d},' elif network_type == lib_const.TYPE_FLAT:
'strip_vlan,resubmit(,{:d})'.format( # If the port belong to flat network, we need match vlan_tci and
ofport, # needn't pop vlan
ovsfw_consts.REG_PORT, actions += 'resubmit(,{:d})'.format(
# This always needs the local vlan. ovs_consts.BASE_INGRESS_TABLE)
local_vlan, self._add_flow(
ovsfw_consts.REG_NET, table=ovs_consts.TRANSIENT_TABLE,
ovs_consts.BASE_INGRESS_TABLE) priority=90,
) dl_dst=mac,
vlan_tci=ovs_consts.FLAT_VLAN_TCI,
actions=actions)
def delete_vlan_direct_flow(self, mac, segment_id): def delete_physical_direct_flow(self, mac, segment_id):
if segment_id: if segment_id:
self._strict_delete_flow(priority=90, self._strict_delete_flow(priority=90,
table=ovs_consts.TRANSIENT_TABLE, table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac, dl_dst=mac,
dl_vlan=segment_id) dl_vlan=segment_id)
else:
self._strict_delete_flow(priority=90,
table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac,
vlan_tci=ovs_consts.FLAT_VLAN_TCI)
def initialize_port_flows(self, port): def initialize_port_flows(self, port):
"""Set base flows for port """Set base flows for port
@ -821,8 +836,9 @@ class OVSFirewallDriver(firewall.FirewallDriver):
# Identify ingress flows # Identify ingress flows
for mac_addr in port.all_allowed_macs: for mac_addr in port.all_allowed_macs:
self.install_vlan_direct_flow( self.install_physical_direct_flow(
mac_addr, port.segment_id, port.ofport, port.vlan_tag) mac_addr, port.segment_id, port.ofport,
port.vlan_tag, port.network_type)
self._add_flow( self._add_flow(
table=ovs_consts.TRANSIENT_TABLE, table=ovs_consts.TRANSIENT_TABLE,
@ -1406,7 +1422,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
table=ovs_consts.TRANSIENT_TABLE, table=ovs_consts.TRANSIENT_TABLE,
dl_dst=mac_addr, dl_dst=mac_addr,
dl_vlan=port.vlan_tag) dl_vlan=port.vlan_tag)
self.delete_vlan_direct_flow(mac_addr, port.segment_id) self.delete_physical_direct_flow(mac_addr, port.segment_id)
self._delete_flows(table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, self._delete_flows(table=ovs_consts.ACCEPT_OR_INGRESS_TABLE,
dl_dst=mac_addr, reg_net=port.vlan_tag) dl_dst=mac_addr, reg_net=port.vlan_tag)

View File

@ -12,9 +12,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
FLAT_VLAN_ID = -1
LOCAL_VLAN_ID = -2
# Supported VXLAN features # Supported VXLAN features
VXLAN_NONE = 'not_supported' VXLAN_NONE = 'not_supported'
VXLAN_MCAST = 'multicast_flooding' VXLAN_MCAST = 'multicast_flooding'

View File

@ -16,8 +16,8 @@
from neutron_lib import constants as p_const from neutron_lib import constants as p_const
# Special vlan_id value in ovs_vlan_allocations table indicating flat network # Special vlan_tci value indicating flat network
FLAT_VLAN_ID = -1 FLAT_VLAN_TCI = '0x0000/0x1fff'
# Topic for tunnel notifications between the plugin and agent # Topic for tunnel notifications between the plugin and agent
TUNNEL = 'tunnel' TUNNEL = 'tunnel'
@ -41,11 +41,14 @@ TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN,
LOCAL_SWITCHING = 0 LOCAL_SWITCHING = 0
# The pyhsical network types of support DVR router
DVR_PHYSICAL_NETWORK_TYPES = [p_const.TYPE_VLAN, p_const.TYPE_FLAT]
# Various tables for DVR use of integration bridge flows # Various tables for DVR use of integration bridge flows
DVR_TO_SRC_MAC = 1 DVR_TO_SRC_MAC = 1
DVR_TO_SRC_MAC_VLAN = 2 DVR_TO_SRC_MAC_PHYSICAL = 2
ARP_DVR_MAC_TO_DST_MAC = 3 ARP_DVR_MAC_TO_DST_MAC = 3
ARP_DVR_MAC_TO_DST_MAC_VLAN = 4 ARP_DVR_MAC_TO_DST_MAC_PHYSICAL = 4
CANARY_TABLE = 23 CANARY_TABLE = 23
# Table for ARP poison/spoofing prevention rules # Table for ARP poison/spoofing prevention rules
@ -82,7 +85,7 @@ ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE = 94
INT_BR_ALL_TABLES = ( INT_BR_ALL_TABLES = (
LOCAL_SWITCHING, LOCAL_SWITCHING,
DVR_TO_SRC_MAC, DVR_TO_SRC_MAC,
DVR_TO_SRC_MAC_VLAN, DVR_TO_SRC_MAC_PHYSICAL,
CANARY_TABLE, CANARY_TABLE,
ARP_SPOOF_TABLE, ARP_SPOOF_TABLE,
MAC_SPOOF_TABLE, MAC_SPOOF_TABLE,
@ -128,15 +131,15 @@ TUN_BR_ALL_TABLES = (
# --- Physical Bridges (phys_brs) # --- Physical Bridges (phys_brs)
# Various tables for DVR use of physical bridge flows # Various tables for DVR use of physical bridge flows
DVR_PROCESS_VLAN = 1 DVR_PROCESS_PHYSICAL = 1
LOCAL_VLAN_TRANSLATION = 2 LOCAL_VLAN_TRANSLATION = 2
DVR_NOT_LEARN_VLAN = 3 DVR_NOT_LEARN_PHYSICAL = 3
PHY_BR_ALL_TABLES = ( PHY_BR_ALL_TABLES = (
LOCAL_SWITCHING, LOCAL_SWITCHING,
DVR_PROCESS_VLAN, DVR_PROCESS_PHYSICAL,
LOCAL_VLAN_TRANSLATION, LOCAL_VLAN_TRANSLATION,
DVR_NOT_LEARN_VLAN) DVR_NOT_LEARN_PHYSICAL)
# --- end of OpenFlow table IDs # --- end of OpenFlow table IDs

View File

@ -21,7 +21,6 @@
import netaddr import netaddr
from neutron_lib import constants as p_const
from os_ken.lib.packet import ether_types from os_ken.lib.packet import ether_types
from os_ken.lib.packet import icmpv6 from os_ken.lib.packet import icmpv6
from os_ken.lib.packet import in_proto from os_ken.lib.packet import in_proto
@ -103,13 +102,15 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
def _arp_dvr_dst_mac_match(ofp, ofpp, vlan, dvr_mac): def _arp_dvr_dst_mac_match(ofp, ofpp, vlan, dvr_mac):
# If eth_dst is equal to the dvr mac of this host, then # If eth_dst is equal to the dvr mac of this host, then
# flag it as matched. # flag it as matched.
if not vlan:
return ofpp.OFPMatch(vlan_vid=ofp.OFPVID_NONE, eth_dst=dvr_mac)
return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT, return ofpp.OFPMatch(vlan_vid=vlan | ofp.OFPVID_PRESENT,
eth_dst=dvr_mac) eth_dst=dvr_mac)
@staticmethod @staticmethod
def _dvr_dst_mac_table_id(network_type): def _dvr_dst_mac_table_id(network_type):
if network_type == p_const.TYPE_VLAN: if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
return constants.ARP_DVR_MAC_TO_DST_MAC_VLAN return constants.ARP_DVR_MAC_TO_DST_MAC_PHYSICAL
else: else:
return constants.ARP_DVR_MAC_TO_DST_MAC return constants.ARP_DVR_MAC_TO_DST_MAC
@ -137,13 +138,16 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
@staticmethod @staticmethod
def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac): def _dvr_to_src_mac_match(ofp, ofpp, vlan_tag, dst_mac):
if not vlan_tag:
# When the network is flat type, the vlan_tag will be None.
return ofpp.OFPMatch(vlan_vid=ofp.OFPVID_NONE, eth_dst=dst_mac)
return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT, return ofpp.OFPMatch(vlan_vid=vlan_tag | ofp.OFPVID_PRESENT,
eth_dst=dst_mac) eth_dst=dst_mac)
@staticmethod @staticmethod
def _dvr_to_src_mac_table_id(network_type): def _dvr_to_src_mac_table_id(network_type):
if network_type == p_const.TYPE_VLAN: if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
return constants.DVR_TO_SRC_MAC_VLAN return constants.DVR_TO_SRC_MAC_PHYSICAL
else: else:
return constants.DVR_TO_SRC_MAC return constants.DVR_TO_SRC_MAC
@ -164,10 +168,10 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
priority=20, priority=20,
match=match, match=match,
instructions=instructions) instructions=instructions)
actions = [ actions = []
ofpp.OFPActionPopVlan(), if vlan_tag:
ofpp.OFPActionOutput(dst_port, 0), actions.append(ofpp.OFPActionPopVlan())
] actions.append(ofpp.OFPActionOutput(dst_port, 0))
self.install_apply_actions(table_id=constants.TRANSIENT_TABLE, self.install_apply_actions(table_id=constants.TRANSIENT_TABLE,
priority=20, priority=20,
match=match, match=match,
@ -182,12 +186,12 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
self.uninstall_flows( self.uninstall_flows(
strict=True, priority=20, table_id=table, match=match) strict=True, priority=20, table_id=table, match=match)
def add_dvr_mac_vlan(self, mac, port): def add_dvr_mac_physical(self, mac, port):
self.install_goto(table_id=constants.LOCAL_SWITCHING, self.install_goto(table_id=constants.LOCAL_SWITCHING,
priority=4, priority=4,
in_port=port, in_port=port,
eth_src=mac, eth_src=mac,
dest_table_id=constants.DVR_TO_SRC_MAC_VLAN) dest_table_id=constants.DVR_TO_SRC_MAC_PHYSICAL)
def remove_dvr_mac_vlan(self, mac): def remove_dvr_mac_vlan(self, mac):
# REVISIT(yamamoto): match in_port as well? # REVISIT(yamamoto): match in_port as well?
@ -214,11 +218,12 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
strict=True, priority=5, table_id=table_id, match=match) strict=True, priority=5, table_id=table_id, match=match)
def add_dvr_gateway_mac_arp_vlan(self, mac, port): def add_dvr_gateway_mac_arp_vlan(self, mac, port):
self.install_goto(table_id=constants.LOCAL_SWITCHING, self.install_goto(
priority=5, table_id=constants.LOCAL_SWITCHING,
in_port=port, priority=5,
eth_dst=mac, in_port=port,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_VLAN) eth_dst=mac,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_PHYSICAL)
def remove_dvr_gateway_mac_arp_vlan(self, mac, port): def remove_dvr_gateway_mac_arp_vlan(self, mac, port):
self.uninstall_flows(table_id=constants.LOCAL_SWITCHING, self.uninstall_flows(table_id=constants.LOCAL_SWITCHING,

View File

@ -26,7 +26,7 @@ class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge,
"""openvswitch agent physical bridge specific logic.""" """openvswitch agent physical bridge specific logic."""
# Used by OVSDVRProcessMixin # Used by OVSDVRProcessMixin
dvr_process_table_id = constants.DVR_PROCESS_VLAN dvr_process_table_id = constants.DVR_PROCESS_PHYSICAL
dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION dvr_process_next_table_id = constants.LOCAL_VLAN_TRANSLATION
of_tables = constants.PHY_BR_ALL_TABLES of_tables = constants.PHY_BR_ALL_TABLES
@ -57,12 +57,12 @@ class OVSPhysicalBridge(ovs_bridge.OVSAgentBridge,
match = self._local_vlan_match(ofp, ofpp, port, lvid) match = self._local_vlan_match(ofp, ofpp, port, lvid)
self.uninstall_flows(match=match) self.uninstall_flows(match=match)
def add_dvr_mac_vlan(self, mac, port): def add_dvr_mac_physical(self, mac, port):
self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN, self.install_output(table_id=constants.DVR_NOT_LEARN_PHYSICAL,
priority=2, eth_src=mac, port=port) priority=2, eth_src=mac, port=port)
def remove_dvr_mac_vlan(self, mac): def remove_dvr_mac_vlan(self, mac):
# REVISIT(yamamoto): match in_port as well? # REVISIT(yamamoto): match in_port as well?
self.uninstall_flows( self.uninstall_flows(
table_id=constants.DVR_NOT_LEARN_VLAN, table_id=constants.DVR_NOT_LEARN_PHYSICAL,
eth_src=mac) eth_src=mac)

View File

@ -224,7 +224,7 @@ class OVSDVRNeutronAgent(object):
# Insert 'drop' action as the default for Table DVR_TO_SRC_MAC # Insert 'drop' action as the default for Table DVR_TO_SRC_MAC
self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1) self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC, priority=1)
self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_PHYSICAL,
priority=1) priority=1)
for physical_network in self.bridge_mappings: for physical_network in self.bridge_mappings:
@ -256,12 +256,12 @@ class OVSDVRNeutronAgent(object):
self.phys_brs[physical_network].install_goto( self.phys_brs[physical_network].install_goto(
in_port=self.phys_ofports[physical_network], in_port=self.phys_ofports[physical_network],
priority=2, priority=2,
dest_table_id=constants.DVR_PROCESS_VLAN) dest_table_id=constants.DVR_PROCESS_PHYSICAL)
self.phys_brs[physical_network].install_goto( self.phys_brs[physical_network].install_goto(
priority=1, priority=1,
dest_table_id=constants.DVR_NOT_LEARN_VLAN) dest_table_id=constants.DVR_NOT_LEARN_PHYSICAL)
self.phys_brs[physical_network].install_goto( self.phys_brs[physical_network].install_goto(
table_id=constants.DVR_PROCESS_VLAN, table_id=constants.DVR_PROCESS_PHYSICAL,
priority=0, priority=0,
dest_table_id=constants.LOCAL_VLAN_TRANSLATION) dest_table_id=constants.LOCAL_VLAN_TRANSLATION)
self.phys_brs[physical_network].install_drop( self.phys_brs[physical_network].install_drop(
@ -269,15 +269,15 @@ class OVSDVRNeutronAgent(object):
in_port=self.phys_ofports[physical_network], in_port=self.phys_ofports[physical_network],
priority=2) priority=2)
self.phys_brs[physical_network].install_normal( self.phys_brs[physical_network].install_normal(
table_id=constants.DVR_NOT_LEARN_VLAN, table_id=constants.DVR_NOT_LEARN_PHYSICAL,
priority=1) priority=1)
def _add_dvr_mac_for_phys_br(self, physical_network, mac): def _add_dvr_mac_for_phys_br(self, physical_network, mac):
self.int_br.add_dvr_mac_vlan(mac=mac, self.int_br.add_dvr_mac_physical(
port=self.int_ofports[physical_network]) mac=mac, port=self.int_ofports[physical_network])
phys_br = self.phys_brs[physical_network] phys_br = self.phys_brs[physical_network]
phys_br.add_dvr_mac_vlan(mac=mac, phys_br.add_dvr_mac_physical(
port=self.phys_ofports[physical_network]) mac=mac, port=self.phys_ofports[physical_network])
def _add_arp_dvr_mac_for_phys_br(self, physical_network, mac): def _add_arp_dvr_mac_for_phys_br(self, physical_network, mac):
self.int_br.add_dvr_gateway_mac_arp_vlan( self.int_br.add_dvr_gateway_mac_arp_vlan(
@ -402,7 +402,7 @@ class OVSDVRNeutronAgent(object):
ldm.set_dvr_owned(True) ldm.set_dvr_owned(True)
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
subnet_info = ldm.get_subnet_info() subnet_info = ldm.get_subnet_info()
@ -456,7 +456,7 @@ class OVSDVRNeutronAgent(object):
dvr_mac=self.dvr_mac_address, dvr_mac=self.dvr_mac_address,
rtr_port=port.ofport) rtr_port=port.ofport)
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
# TODO(vivek) remove the IPv6 related flows once SNAT is not # TODO(vivek) remove the IPv6 related flows once SNAT is not
# used for IPv6 DVR. # used for IPv6 DVR.
br = self.phys_brs[lvm.physical_network] br = self.phys_brs[lvm.physical_network]
@ -517,7 +517,7 @@ class OVSDVRNeutronAgent(object):
ovsport.add_subnet(subnet_uuid) ovsport.add_subnet(subnet_uuid)
self.local_ports[port.vif_id] = ovsport self.local_ports[port.vif_id] = ovsport
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
# create a rule for this vm port # create a rule for this vm port
self.int_br.install_dvr_to_src_mac( self.int_br.install_dvr_to_src_mac(
@ -577,7 +577,7 @@ class OVSDVRNeutronAgent(object):
ovsport.add_subnet(subnet_uuid) ovsport.add_subnet(subnet_uuid)
self.local_ports[port.vif_id] = ovsport self.local_ports[port.vif_id] = ovsport
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
self.int_br.install_dvr_to_src_mac( self.int_br.install_dvr_to_src_mac(
network_type=lvm.network_type, network_type=lvm.network_type,
@ -591,8 +591,9 @@ class OVSDVRNeutronAgent(object):
if not self.in_distributed_mode(): if not self.in_distributed_mode():
return return
if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES + if (local_vlan_map.network_type not in
[n_const.TYPE_VLAN]): (constants.TUNNEL_NETWORK_TYPES +
constants.DVR_PHYSICAL_NETWORK_TYPES)):
LOG.debug("DVR: Port %s is with network_type %s not supported" LOG.debug("DVR: Port %s is with network_type %s not supported"
" for dvr plumbing", port.vif_id, " for dvr plumbing", port.vif_id,
local_vlan_map.network_type) local_vlan_map.network_type)
@ -629,7 +630,7 @@ class OVSDVRNeutronAgent(object):
network_type = lvm.network_type network_type = lvm.network_type
physical_network = lvm.physical_network physical_network = lvm.physical_network
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if network_type == n_const.TYPE_VLAN: if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
# ensure we process for all the subnets laid on this removed port # ensure we process for all the subnets laid on this removed port
for sub_uuid in subnet_set: for sub_uuid in subnet_set:
@ -660,7 +661,7 @@ class OVSDVRNeutronAgent(object):
# this subnet from local_dvr_map, as no dvr (or) csnat # this subnet from local_dvr_map, as no dvr (or) csnat
# ports available on this agent anymore # ports available on this agent anymore
self.local_dvr_map.pop(sub_uuid, None) self.local_dvr_map.pop(sub_uuid, None)
if network_type == n_const.TYPE_VLAN: if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
br = self.phys_brs[physical_network] br = self.phys_brs[physical_network]
if network_type in constants.TUNNEL_NETWORK_TYPES: if network_type in constants.TUNNEL_NETWORK_TYPES:
br = self.tun_br br = self.tun_br
@ -679,7 +680,7 @@ class OVSDVRNeutronAgent(object):
self.firewall.delete_accepted_egress_direct_flow( self.firewall.delete_accepted_egress_direct_flow(
subnet_info['gateway_mac'], lvm.vlan) subnet_info['gateway_mac'], lvm.vlan)
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
br = self.phys_brs[physical_network] br = self.phys_brs[physical_network]
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
br = self.tun_br br = self.tun_br
@ -701,7 +702,7 @@ class OVSDVRNeutronAgent(object):
ldm = self.local_dvr_map[sub_uuid] ldm = self.local_dvr_map[sub_uuid]
ldm.remove_compute_ofport(port.vif_id) ldm.remove_compute_ofport(port.vif_id)
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
# first remove this vm port rule # first remove this vm port rule
self.int_br.delete_dvr_to_src_mac( self.int_br.delete_dvr_to_src_mac(
@ -722,7 +723,7 @@ class OVSDVRNeutronAgent(object):
ldm = self.local_dvr_map[sub_uuid] ldm = self.local_dvr_map[sub_uuid]
ldm.set_csnat_ofport(constants.OFPORT_INVALID) ldm.set_csnat_ofport(constants.OFPORT_INVALID)
vlan_to_use = lvm.vlan vlan_to_use = lvm.vlan
if lvm.network_type == n_const.TYPE_VLAN: if lvm.network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
vlan_to_use = lvm.segmentation_id vlan_to_use = lvm.segmentation_id
# then remove csnat port rule # then remove csnat port rule
self.int_br.delete_dvr_to_src_mac( self.int_br.delete_dvr_to_src_mac(

View File

@ -901,18 +901,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
else: else:
LOG.warning('Action %s not supported', action) LOG.warning('Action %s not supported', action)
def _local_vlan_for_flat(self, lvid, physical_network): def _local_vlan_for_physical(self, lvid, physical_network,
phys_br = self.phys_brs[physical_network] segmentation_id=None):
phys_port = self.phys_ofports[physical_network]
int_br = self.int_br
int_port = self.int_ofports[physical_network]
phys_br.provision_local_vlan(port=phys_port, lvid=lvid,
segmentation_id=None,
distributed=False)
int_br.provision_local_vlan(port=int_port, lvid=lvid,
segmentation_id=None)
def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id):
distributed = self.enable_distributed_routing distributed = self.enable_distributed_routing
phys_br = self.phys_brs[physical_network] phys_br = self.phys_brs[physical_network]
phys_port = self.phys_ofports[physical_network] phys_port = self.phys_ofports[physical_network]
@ -993,7 +983,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
'net_uuid': net_uuid}) 'net_uuid': net_uuid})
elif network_type == n_const.TYPE_FLAT: elif network_type == n_const.TYPE_FLAT:
if physical_network in self.phys_brs: if physical_network in self.phys_brs:
self._local_vlan_for_flat(lvid, physical_network) self._local_vlan_for_physical(lvid, physical_network)
else: else:
LOG.error("Cannot provision flat network for " LOG.error("Cannot provision flat network for "
"net-id=%(net_uuid)s - no bridge for " "net-id=%(net_uuid)s - no bridge for "
@ -1002,8 +992,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
'physical_network': physical_network}) 'physical_network': physical_network})
elif network_type == n_const.TYPE_VLAN: elif network_type == n_const.TYPE_VLAN:
if physical_network in self.phys_brs: if physical_network in self.phys_brs:
self._local_vlan_for_vlan(lvid, physical_network, self._local_vlan_for_physical(lvid, physical_network,
segmentation_id) segmentation_id)
else: else:
LOG.error("Cannot provision VLAN network for " LOG.error("Cannot provision VLAN network for "
"net-id=%(net_uuid)s - no bridge for " "net-id=%(net_uuid)s - no bridge for "

View File

@ -344,7 +344,8 @@ class OVSFlowTestCase(OVSAgentTestBase):
'dst_mac': '12:34:56:78:cc:dd', 'dst_mac': '12:34:56:78:cc:dd',
'dst_port': 123} 'dst_port': 123}
self.br_int.install_dvr_to_src_mac(network_type='vlan', **kwargs) self.br_int.install_dvr_to_src_mac(network_type='vlan', **kwargs)
self.br_int.add_dvr_mac_vlan(mac=other_dvr_mac, port=other_dvr_port) self.br_int.add_dvr_mac_physical(mac=other_dvr_mac,
port=other_dvr_port)
trace = self._run_trace(self.br.br_name, trace = self._run_trace(self.br.br_name,
"in_port=%d," % other_dvr_port + "in_port=%d," % other_dvr_port +

View File

@ -38,11 +38,12 @@ TESTING_VLAN_TAG = 1
TESTING_SEGMENT = 1000 TESTING_SEGMENT = 1000
def create_ofport(port_dict, network_type=None, physical_network=None): def create_ofport(port_dict, network_type=None,
physical_network=None, segment_id=TESTING_SEGMENT):
ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1, ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1,
port_name="port-name") port_name="port-name")
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG, return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG,
segment_id=TESTING_SEGMENT, segment_id=segment_id,
network_type=network_type, network_type=network_type,
physical_network=physical_network) physical_network=physical_network)
@ -590,18 +591,23 @@ class TestOVSFirewallDriver(base.BaseTestCase):
self.firewall.prepare_port_filter(port_dict) self.firewall.prepare_port_filter(port_dict)
self.assertFalse(m_init_flows.called) self.assertFalse(m_init_flows.called)
def test_initialize_port_flows_vlan_dvr_conntrack_direct(self): def _test_initialize_port_flows_dvr_conntrack_direct(self, network_type):
port_dict = { port_dict = {
'device': 'port-id', 'device': 'port-id',
'security_groups': [1]} 'security_groups': [1]}
segment_id = None
if network_type == constants.TYPE_VLAN:
segment_id = TESTING_SEGMENT
of_port = create_ofport(port_dict, of_port = create_ofport(port_dict,
network_type=constants.TYPE_VXLAN) network_type=network_type,
segment_id=segment_id)
self.firewall.sg_port_map.ports[of_port.id] = of_port self.firewall.sg_port_map.ports[of_port.id] = of_port
port = self.firewall.get_or_create_ofport(port_dict) port = self.firewall.get_or_create_ofport(port_dict)
fake_patch_port = 999 fake_patch_port = 999
self.mock_bridge.br.get_port_ofport.return_value = fake_patch_port self.mock_bridge.br.get_port_ofport.return_value = fake_patch_port
expected_calls = []
self.firewall.initialize_port_flows(port) self.firewall.initialize_port_flows(port)
call_args1 = { call_args1 = {
@ -616,22 +622,39 @@ class TestOVSFirewallDriver(base.BaseTestCase):
port.vlan_tag, port.vlan_tag,
ovsfw_consts.REG_NET, ovsfw_consts.REG_NET,
ovs_consts.BASE_EGRESS_TABLE)} ovs_consts.BASE_EGRESS_TABLE)}
egress_flow_call = mock.call(**call_args1) expected_calls.append(mock.call(**call_args1))
call_args2 = { if network_type == constants.TYPE_VLAN:
'table': ovs_consts.TRANSIENT_TABLE, call_args2 = {
'priority': 90, 'table': ovs_consts.TRANSIENT_TABLE,
'dl_dst': port.mac, 'priority': 90,
'dl_vlan': '0x%x' % port.segment_id, 'dl_dst': port.mac,
'actions': 'set_field:{:d}->reg{:d},' 'dl_vlan': '0x%x' % port.segment_id,
'set_field:{:d}->reg{:d},' 'actions': 'set_field:{:d}->reg{:d},'
'strip_vlan,resubmit(,{:d})'.format( 'set_field:{:d}->reg{:d},'
port.ofport, 'strip_vlan,resubmit(,{:d})'.format(
ovsfw_consts.REG_PORT, port.ofport,
port.vlan_tag, ovsfw_consts.REG_PORT,
ovsfw_consts.REG_NET, port.vlan_tag,
ovs_consts.BASE_INGRESS_TABLE)} ovsfw_consts.REG_NET,
ingress_flow_call1 = mock.call(**call_args2) ovs_consts.BASE_INGRESS_TABLE)}
expected_calls.append(mock.call(**call_args2))
if network_type == constants.TYPE_FLAT:
call_args2 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 90,
'dl_dst': port.mac,
'vlan_tci': ovs_consts.FLAT_VLAN_TCI,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'resubmit(,{:d})'.format(
port.ofport,
ovsfw_consts.REG_PORT,
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)}
expected_calls.append(mock.call(**call_args2))
call_args3 = { call_args3 = {
'table': ovs_consts.TRANSIENT_TABLE, 'table': ovs_consts.TRANSIENT_TABLE,
@ -646,9 +669,20 @@ class TestOVSFirewallDriver(base.BaseTestCase):
port.vlan_tag, port.vlan_tag,
ovsfw_consts.REG_NET, ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)} ovs_consts.BASE_INGRESS_TABLE)}
ingress_flow_call2 = mock.call(**call_args3) expected_calls.append(mock.call(**call_args3))
self.mock_bridge.br.add_flow.assert_has_calls( self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
[egress_flow_call, ingress_flow_call1, ingress_flow_call2])
def test_initialize_port_flows_dvr_conntrack_direct_vxlan(self):
self._test_initialize_port_flows_dvr_conntrack_direct(
network_type='vxlan')
def test_initialize_port_flows_dvr_conntrack_direct_vlan(self):
self._test_initialize_port_flows_dvr_conntrack_direct(
network_type='vlan')
def test_initialize_port_flows_dvr_conntrack_direct_flat(self):
self._test_initialize_port_flows_dvr_conntrack_direct(
network_type='flat')
def test_initialize_port_flows_vlan_dvr_conntrack_direct_vlan(self): def test_initialize_port_flows_vlan_dvr_conntrack_direct_vlan(self):
port_dict = { port_dict = {

View File

@ -286,6 +286,48 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
] ]
self.assertEqual(expected, self.mock.mock_calls) self.assertEqual(expected, self.mock.mock_calls)
def test_install_dvr_to_src_mac_flat(self):
network_type = 'flat'
gateway_mac = '08:60:6e:7f:74:e7'
dst_mac = '00:02:b3:13:fe:3d'
dst_port = 6666
self.br.install_dvr_to_src_mac(network_type=network_type,
vlan_tag=None,
gateway_mac=gateway_mac,
dst_mac=dst_mac,
dst_port=dst_port)
(dp, ofp, ofpp) = self._get_dp()
expected = [
call._send_msg(ofpp.OFPFlowMod(dp,
cookie=self.stamp,
instructions=[
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
ofpp.OFPActionSetField(eth_src=gateway_mac),
]),
ofpp.OFPInstructionGotoTable(table_id=60),
],
match=ofpp.OFPMatch(
eth_dst=dst_mac,
vlan_vid=ofp.OFPVID_NONE),
priority=20,
table_id=2),
active_bundle=None),
call._send_msg(ofpp.OFPFlowMod(dp,
cookie=self.stamp,
instructions=[
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
ofpp.OFPActionOutput(dst_port, 0),
]),
],
match=ofpp.OFPMatch(
eth_dst=dst_mac,
vlan_vid=ofp.OFPVID_NONE),
priority=20,
table_id=60),
active_bundle=None),
]
self.assertEqual(expected, self.mock.mock_calls)
def test_delete_dvr_to_src_mac_vlan(self): def test_delete_dvr_to_src_mac_vlan(self):
network_type = 'vlan' network_type = 'vlan'
vlan_tag = 1111 vlan_tag = 1111
@ -312,10 +354,36 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
] ]
self.assertEqual(expected, self.mock.mock_calls) self.assertEqual(expected, self.mock.mock_calls)
def test_add_dvr_mac_vlan(self): def test_delete_dvr_to_src_mac_flat(self):
network_type = 'flat'
vlan_tag = None
dst_mac = '00:02:b3:13:fe:3d'
self.br.delete_dvr_to_src_mac(network_type=network_type,
vlan_tag=vlan_tag,
dst_mac=dst_mac)
(dp, ofp, ofpp) = self._get_dp()
expected = [
call.uninstall_flows(
strict=True,
priority=20,
table_id=2,
match=ofpp.OFPMatch(
eth_dst=dst_mac,
vlan_vid=ofp.OFPVID_NONE)),
call.uninstall_flows(
strict=True,
priority=20,
table_id=60,
match=ofpp.OFPMatch(
eth_dst=dst_mac,
vlan_vid=ofp.OFPVID_NONE)),
]
self.assertEqual(expected, self.mock.mock_calls)
def test_add_dvr_mac_physical(self):
mac = '00:02:b3:13:fe:3d' mac = '00:02:b3:13:fe:3d'
port = 8888 port = 8888
self.br.add_dvr_mac_vlan(mac=mac, port=port) self.br.add_dvr_mac_physical(mac=mac, port=port)
(dp, ofp, ofpp) = self._get_dp() (dp, ofp, ofpp) = self._get_dp()
expected = [ expected = [
call._send_msg(ofpp.OFPFlowMod(dp, call._send_msg(ofpp.OFPFlowMod(dp,
@ -488,12 +556,15 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
self.assertEqual(expected, self.mock.mock_calls) self.assertEqual(expected, self.mock.mock_calls)
def _test_delete_dvr_dst_mac_for_arp(self, network_type): def _test_delete_dvr_dst_mac_for_arp(self, network_type):
if network_type == p_const.TYPE_VLAN: if network_type in (p_const.TYPE_VLAN, p_const.TYPE_FLAT):
table_id = constants.DVR_TO_SRC_MAC_VLAN table_id = constants.DVR_TO_SRC_MAC_PHYSICAL
else: else:
table_id = constants.DVR_TO_SRC_MAC table_id = constants.DVR_TO_SRC_MAC
vlan_tag = 1111 if network_type == p_const.TYPE_FLAT:
vlan_tag = None
else:
vlan_tag = 1111
gateway_mac = '00:02:b3:13:fe:3e' gateway_mac = '00:02:b3:13:fe:3e'
dvr_mac = '00:02:b3:13:fe:3f' dvr_mac = '00:02:b3:13:fe:3f'
rtr_port = 8888 rtr_port = 8888
@ -503,15 +574,26 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
dvr_mac=dvr_mac, dvr_mac=dvr_mac,
rtr_port=rtr_port) rtr_port=rtr_port)
(dp, ofp, ofpp) = self._get_dp() (dp, ofp, ofpp) = self._get_dp()
expected = [ if network_type == p_const.TYPE_FLAT:
call.uninstall_flows( expected = [
strict=True, call.uninstall_flows(
priority=5, strict=True,
table_id=table_id, priority=5,
match=ofpp.OFPMatch( table_id=table_id,
eth_dst=dvr_mac, match=ofpp.OFPMatch(
vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)), eth_dst=dvr_mac,
] vlan_vid=ofp.OFPVID_NONE)),
]
else:
expected = [
call.uninstall_flows(
strict=True,
priority=5,
table_id=table_id,
match=ofpp.OFPMatch(
eth_dst=dvr_mac,
vlan_vid=vlan_tag | ofp.OFPVID_PRESENT)),
]
self.assertEqual(expected, self.mock.mock_calls) self.assertEqual(expected, self.mock.mock_calls)
def test_delete_dvr_dst_mac_for_arp_vlan(self): def test_delete_dvr_dst_mac_for_arp_vlan(self):
@ -520,6 +602,9 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
def test_delete_dvr_dst_mac_for_arp_tunnel(self): def test_delete_dvr_dst_mac_for_arp_tunnel(self):
self._test_delete_dvr_dst_mac_for_arp(network_type='vxlan') self._test_delete_dvr_dst_mac_for_arp(network_type='vxlan')
def test_delete_dvr_dst_mac_for_flat(self):
self._test_delete_dvr_dst_mac_for_arp(network_type='flat')
def test_install_dscp_marking_rule(self): def test_install_dscp_marking_rule(self):
test_port = 8888 test_port = 8888
test_mark = 38 test_mark = 38

View File

@ -27,7 +27,7 @@ call = mock.call # short hand
class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase, class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
ovs_bridge_test_base.OVSDVRProcessTestMixin): ovs_bridge_test_base.OVSDVRProcessTestMixin):
dvr_process_table_id = ovs_const.DVR_PROCESS_VLAN dvr_process_table_id = ovs_const.DVR_PROCESS_PHYSICAL
dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION dvr_process_next_table_id = ovs_const.LOCAL_VLAN_TRANSLATION
def setUp(self): def setUp(self):
@ -125,10 +125,10 @@ class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
] ]
self.assertEqual(expected, self.mock.mock_calls) self.assertEqual(expected, self.mock.mock_calls)
def test_add_dvr_mac_vlan(self): def test_add_dvr_mac_physical(self):
mac = '00:02:b3:13:fe:3d' mac = '00:02:b3:13:fe:3d'
port = 8888 port = 8888
self.br.add_dvr_mac_vlan(mac=mac, port=port) self.br.add_dvr_mac_physical(mac=mac, port=port)
(dp, ofp, ofpp) = self._get_dp() (dp, ofp, ofpp) = self._get_dp()
expected = [ expected = [
call._send_msg(ofpp.OFPFlowMod(dp, call._send_msg(ofpp.OFPFlowMod(dp,

View File

@ -3078,8 +3078,8 @@ class TestOvsDvrNeutronAgent(object):
), ),
] ]
def _test_port_bound_for_dvr_on_vlan_network( def _test_port_bound_for_dvr_on_physical_network(
self, device_owner, ip_version=n_const.IP_VERSION_4): self, device_owner, network_type, ip_version=n_const.IP_VERSION_4):
self._setup_for_dvr_test() self._setup_for_dvr_test()
if ip_version == n_const.IP_VERSION_4: if ip_version == n_const.IP_VERSION_4:
gateway_ip = '1.1.1.10' gateway_ip = '1.1.1.10'
@ -3093,7 +3093,8 @@ class TestOvsDvrNeutronAgent(object):
self._compute_port.vif_mac = '77:88:99:00:11:22' self._compute_port.vif_mac = '77:88:99:00:11:22'
physical_network = self._physical_network physical_network = self._physical_network
segmentation_id = self._segmentation_id segmentation_id = self._segmentation_id
network_type = n_const.TYPE_VLAN if network_type == n_const.TYPE_FLAT:
segmentation_id = None
int_br = mock.create_autospec(self.agent.int_br) int_br = mock.create_autospec(self.agent.int_br)
tun_br = mock.create_autospec(self.agent.tun_br) tun_br = mock.create_autospec(self.agent.tun_br)
phys_br = mock.create_autospec(self.br_phys_cls('br-phys')) phys_br = mock.create_autospec(self.br_phys_cls('br-phys'))
@ -3256,10 +3257,19 @@ class TestOvsDvrNeutronAgent(object):
phys_br.assert_not_called() phys_br.assert_not_called()
def test_port_bound_for_dvr_with_compute_ports(self): def test_port_bound_for_dvr_with_compute_ports(self):
self._test_port_bound_for_dvr_on_vlan_network( self._test_port_bound_for_dvr_on_physical_network(
device_owner=DEVICE_OWNER_COMPUTE)
self._test_port_bound_for_dvr_on_vlan_network(
device_owner=DEVICE_OWNER_COMPUTE, device_owner=DEVICE_OWNER_COMPUTE,
network_type=n_const.TYPE_VLAN)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=DEVICE_OWNER_COMPUTE,
network_type=n_const.TYPE_VLAN,
ip_version=n_const.IP_VERSION_6)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=DEVICE_OWNER_COMPUTE,
network_type=n_const.TYPE_FLAT)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=DEVICE_OWNER_COMPUTE,
network_type=n_const.TYPE_FLAT,
ip_version=n_const.IP_VERSION_6) ip_version=n_const.IP_VERSION_6)
self._test_port_bound_for_dvr_on_vxlan_network( self._test_port_bound_for_dvr_on_vxlan_network(
device_owner=DEVICE_OWNER_COMPUTE) device_owner=DEVICE_OWNER_COMPUTE)
@ -3268,10 +3278,19 @@ class TestOvsDvrNeutronAgent(object):
ip_version=n_const.IP_VERSION_6) ip_version=n_const.IP_VERSION_6)
def test_port_bound_for_dvr_with_dhcp_ports(self): def test_port_bound_for_dvr_with_dhcp_ports(self):
self._test_port_bound_for_dvr_on_vlan_network( self._test_port_bound_for_dvr_on_physical_network(
device_owner=n_const.DEVICE_OWNER_DHCP)
self._test_port_bound_for_dvr_on_vlan_network(
device_owner=n_const.DEVICE_OWNER_DHCP, device_owner=n_const.DEVICE_OWNER_DHCP,
network_type=n_const.TYPE_VLAN)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=n_const.DEVICE_OWNER_DHCP,
network_type=n_const.TYPE_VLAN,
ip_version=n_const.IP_VERSION_6)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=n_const.DEVICE_OWNER_DHCP,
network_type=n_const.TYPE_FLAT)
self._test_port_bound_for_dvr_on_physical_network(
device_owner=n_const.DEVICE_OWNER_DHCP,
network_type=n_const.TYPE_FLAT,
ip_version=n_const.IP_VERSION_6) ip_version=n_const.IP_VERSION_6)
self._test_port_bound_for_dvr_on_vxlan_network( self._test_port_bound_for_dvr_on_vxlan_network(
device_owner=n_const.DEVICE_OWNER_DHCP) device_owner=n_const.DEVICE_OWNER_DHCP)
@ -3737,8 +3756,9 @@ class TestOvsDvrNeutronAgent(object):
mock.call.setup_canary_table(), mock.call.setup_canary_table(),
mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC, mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC,
priority=1), priority=1),
mock.call.install_drop(table_id=constants.DVR_TO_SRC_MAC_VLAN, mock.call.install_drop(
priority=1), table_id=constants.DVR_TO_SRC_MAC_PHYSICAL,
priority=1),
mock.call.install_drop(table_id=constants.LOCAL_SWITCHING, mock.call.install_drop(table_id=constants.LOCAL_SWITCHING,
priority=2, priority=2,
in_port=ioport), in_port=ioport),
@ -3829,7 +3849,7 @@ class TestOvsDvrNeutronAgent(object):
dvr_macs=[{'host': newhost, dvr_macs=[{'host': newhost,
'mac_address': newmac}]) 'mac_address': newmac}])
expected_on_int_br = [ expected_on_int_br = [
mock.call.add_dvr_mac_vlan( mock.call.add_dvr_mac_physical(
mac=newmac, mac=newmac,
port=self.agent.int_ofports[physical_network]), port=self.agent.int_ofports[physical_network]),
mock.call.add_dvr_mac_tun( mock.call.add_dvr_mac_tun(
@ -3842,7 +3862,7 @@ class TestOvsDvrNeutronAgent(object):
port=self.agent.patch_int_ofport), port=self.agent.patch_int_ofport),
] ]
expected_on_phys_br = [ expected_on_phys_br = [
mock.call.add_dvr_mac_vlan( mock.call.add_dvr_mac_physical(
mac=newmac, mac=newmac,
port=self.agent.phys_ofports[physical_network]), port=self.agent.phys_ofports[physical_network]),
] ]

View File

@ -0,0 +1,9 @@
---
features:
- |
``DVR`` routers now support ``flat`` networks.
fixes:
- |
Fixed bug `1876092 <https://bugs.launchpad.net/neutron/+bug/1876092>`_
which caused DUP ICMP replies on the ``flat`` networks used with ``DVR``
routers.