Make DVR router support FLAT network for ovs-agent

Currently codes only support assocate tunnel network and vlan network
to DVR router. This patch add codes that make the flat network assocate
to DVR router and make it work fine.

The patch also remove two unused constant entries: 'FLAT_VLAN_ID' and
'LOCAL_VLAN_ID'

Change-Id: I7d792ce288d96548298f169748565266a130bd86
Closes-Bug: #1876092
This commit is contained in:
Yang JianFeng 2020-05-07 05:36:31 +00:00
parent 64b92687a5
commit cd721a7dcb
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

@ -896,18 +896,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]
@ -988,7 +978,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 "
@ -997,8 +987,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.