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

View File

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

View File

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

View File

@ -21,7 +21,6 @@
import netaddr
from neutron_lib import constants as p_const
from os_ken.lib.packet import ether_types
from os_ken.lib.packet import icmpv6
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):
# If eth_dst is equal to the dvr mac of this host, then
# 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,
eth_dst=dvr_mac)
@staticmethod
def _dvr_dst_mac_table_id(network_type):
if network_type == p_const.TYPE_VLAN:
return constants.ARP_DVR_MAC_TO_DST_MAC_VLAN
if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
return constants.ARP_DVR_MAC_TO_DST_MAC_PHYSICAL
else:
return constants.ARP_DVR_MAC_TO_DST_MAC
@ -137,13 +138,16 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
@staticmethod
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,
eth_dst=dst_mac)
@staticmethod
def _dvr_to_src_mac_table_id(network_type):
if network_type == p_const.TYPE_VLAN:
return constants.DVR_TO_SRC_MAC_VLAN
if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
return constants.DVR_TO_SRC_MAC_PHYSICAL
else:
return constants.DVR_TO_SRC_MAC
@ -164,10 +168,10 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
priority=20,
match=match,
instructions=instructions)
actions = [
ofpp.OFPActionPopVlan(),
ofpp.OFPActionOutput(dst_port, 0),
]
actions = []
if vlan_tag:
actions.append(ofpp.OFPActionPopVlan())
actions.append(ofpp.OFPActionOutput(dst_port, 0))
self.install_apply_actions(table_id=constants.TRANSIENT_TABLE,
priority=20,
match=match,
@ -182,12 +186,12 @@ class OVSIntegrationBridge(ovs_bridge.OVSAgentBridge):
self.uninstall_flows(
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,
priority=4,
in_port=port,
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):
# 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)
def add_dvr_gateway_mac_arp_vlan(self, mac, port):
self.install_goto(table_id=constants.LOCAL_SWITCHING,
priority=5,
in_port=port,
eth_dst=mac,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_VLAN)
self.install_goto(
table_id=constants.LOCAL_SWITCHING,
priority=5,
in_port=port,
eth_dst=mac,
dest_table_id=constants.ARP_DVR_MAC_TO_DST_MAC_PHYSICAL)
def remove_dvr_gateway_mac_arp_vlan(self, mac, port):
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."""
# 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
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)
self.uninstall_flows(match=match)
def add_dvr_mac_vlan(self, mac, port):
self.install_output(table_id=constants.DVR_NOT_LEARN_VLAN,
def add_dvr_mac_physical(self, mac, port):
self.install_output(table_id=constants.DVR_NOT_LEARN_PHYSICAL,
priority=2, eth_src=mac, port=port)
def remove_dvr_mac_vlan(self, mac):
# REVISIT(yamamoto): match in_port as well?
self.uninstall_flows(
table_id=constants.DVR_NOT_LEARN_VLAN,
table_id=constants.DVR_NOT_LEARN_PHYSICAL,
eth_src=mac)

View File

@ -224,7 +224,7 @@ class OVSDVRNeutronAgent(object):
# 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_VLAN,
self.int_br.install_drop(table_id=constants.DVR_TO_SRC_MAC_PHYSICAL,
priority=1)
for physical_network in self.bridge_mappings:
@ -256,12 +256,12 @@ class OVSDVRNeutronAgent(object):
self.phys_brs[physical_network].install_goto(
in_port=self.phys_ofports[physical_network],
priority=2,
dest_table_id=constants.DVR_PROCESS_VLAN)
dest_table_id=constants.DVR_PROCESS_PHYSICAL)
self.phys_brs[physical_network].install_goto(
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(
table_id=constants.DVR_PROCESS_VLAN,
table_id=constants.DVR_PROCESS_PHYSICAL,
priority=0,
dest_table_id=constants.LOCAL_VLAN_TRANSLATION)
self.phys_brs[physical_network].install_drop(
@ -269,15 +269,15 @@ class OVSDVRNeutronAgent(object):
in_port=self.phys_ofports[physical_network],
priority=2)
self.phys_brs[physical_network].install_normal(
table_id=constants.DVR_NOT_LEARN_VLAN,
table_id=constants.DVR_NOT_LEARN_PHYSICAL,
priority=1)
def _add_dvr_mac_for_phys_br(self, physical_network, mac):
self.int_br.add_dvr_mac_vlan(mac=mac,
port=self.int_ofports[physical_network])
self.int_br.add_dvr_mac_physical(
mac=mac, port=self.int_ofports[physical_network])
phys_br = self.phys_brs[physical_network]
phys_br.add_dvr_mac_vlan(mac=mac,
port=self.phys_ofports[physical_network])
phys_br.add_dvr_mac_physical(
mac=mac, port=self.phys_ofports[physical_network])
def _add_arp_dvr_mac_for_phys_br(self, physical_network, mac):
self.int_br.add_dvr_gateway_mac_arp_vlan(
@ -402,7 +402,7 @@ class OVSDVRNeutronAgent(object):
ldm.set_dvr_owned(True)
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
subnet_info = ldm.get_subnet_info()
@ -456,7 +456,7 @@ class OVSDVRNeutronAgent(object):
dvr_mac=self.dvr_mac_address,
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
# used for IPv6 DVR.
br = self.phys_brs[lvm.physical_network]
@ -517,7 +517,7 @@ class OVSDVRNeutronAgent(object):
ovsport.add_subnet(subnet_uuid)
self.local_ports[port.vif_id] = ovsport
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
# create a rule for this vm port
self.int_br.install_dvr_to_src_mac(
@ -577,7 +577,7 @@ class OVSDVRNeutronAgent(object):
ovsport.add_subnet(subnet_uuid)
self.local_ports[port.vif_id] = ovsport
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
self.int_br.install_dvr_to_src_mac(
network_type=lvm.network_type,
@ -591,8 +591,9 @@ class OVSDVRNeutronAgent(object):
if not self.in_distributed_mode():
return
if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES +
[n_const.TYPE_VLAN]):
if (local_vlan_map.network_type not in
(constants.TUNNEL_NETWORK_TYPES +
constants.DVR_PHYSICAL_NETWORK_TYPES)):
LOG.debug("DVR: Port %s is with network_type %s not supported"
" for dvr plumbing", port.vif_id,
local_vlan_map.network_type)
@ -629,7 +630,7 @@ class OVSDVRNeutronAgent(object):
network_type = lvm.network_type
physical_network = lvm.physical_network
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
# ensure we process for all the subnets laid on this removed port
for sub_uuid in subnet_set:
@ -660,7 +661,7 @@ class OVSDVRNeutronAgent(object):
# 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)
if network_type == n_const.TYPE_VLAN:
if network_type in constants.DVR_PHYSICAL_NETWORK_TYPES:
br = self.phys_brs[physical_network]
if network_type in constants.TUNNEL_NETWORK_TYPES:
br = self.tun_br
@ -679,7 +680,7 @@ class OVSDVRNeutronAgent(object):
self.firewall.delete_accepted_egress_direct_flow(
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]
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
br = self.tun_br
@ -701,7 +702,7 @@ class OVSDVRNeutronAgent(object):
ldm = self.local_dvr_map[sub_uuid]
ldm.remove_compute_ofport(port.vif_id)
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
# first remove this vm port rule
self.int_br.delete_dvr_to_src_mac(
@ -722,7 +723,7 @@ class OVSDVRNeutronAgent(object):
ldm = self.local_dvr_map[sub_uuid]
ldm.set_csnat_ofport(constants.OFPORT_INVALID)
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
# then remove csnat port rule
self.int_br.delete_dvr_to_src_mac(

View File

@ -896,18 +896,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
else:
LOG.warning('Action %s not supported', action)
def _local_vlan_for_flat(self, lvid, physical_network):
phys_br = self.phys_brs[physical_network]
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):
def _local_vlan_for_physical(self, lvid, physical_network,
segmentation_id=None):
distributed = self.enable_distributed_routing
phys_br = self.phys_brs[physical_network]
phys_port = self.phys_ofports[physical_network]
@ -988,7 +978,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
'net_uuid': net_uuid})
elif network_type == n_const.TYPE_FLAT:
if physical_network in self.phys_brs:
self._local_vlan_for_flat(lvid, physical_network)
self._local_vlan_for_physical(lvid, physical_network)
else:
LOG.error("Cannot provision flat network for "
"net-id=%(net_uuid)s - no bridge for "
@ -997,8 +987,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
'physical_network': physical_network})
elif network_type == n_const.TYPE_VLAN:
if physical_network in self.phys_brs:
self._local_vlan_for_vlan(lvid, physical_network,
segmentation_id)
self._local_vlan_for_physical(lvid, physical_network,
segmentation_id)
else:
LOG.error("Cannot provision VLAN network 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_port': 123}
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,
"in_port=%d," % other_dvr_port +

View File

@ -38,11 +38,12 @@ TESTING_VLAN_TAG = 1
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,
port_name="port-name")
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG,
segment_id=TESTING_SEGMENT,
segment_id=segment_id,
network_type=network_type,
physical_network=physical_network)
@ -590,18 +591,23 @@ class TestOVSFirewallDriver(base.BaseTestCase):
self.firewall.prepare_port_filter(port_dict)
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 = {
'device': 'port-id',
'security_groups': [1]}
segment_id = None
if network_type == constants.TYPE_VLAN:
segment_id = TESTING_SEGMENT
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
port = self.firewall.get_or_create_ofport(port_dict)
fake_patch_port = 999
self.mock_bridge.br.get_port_ofport.return_value = fake_patch_port
expected_calls = []
self.firewall.initialize_port_flows(port)
call_args1 = {
@ -616,22 +622,39 @@ class TestOVSFirewallDriver(base.BaseTestCase):
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_EGRESS_TABLE)}
egress_flow_call = mock.call(**call_args1)
expected_calls.append(mock.call(**call_args1))
call_args2 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 90,
'dl_dst': port.mac,
'dl_vlan': '0x%x' % port.segment_id,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'strip_vlan,resubmit(,{:d})'.format(
port.ofport,
ovsfw_consts.REG_PORT,
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)}
ingress_flow_call1 = mock.call(**call_args2)
if network_type == constants.TYPE_VLAN:
call_args2 = {
'table': ovs_consts.TRANSIENT_TABLE,
'priority': 90,
'dl_dst': port.mac,
'dl_vlan': '0x%x' % port.segment_id,
'actions': 'set_field:{:d}->reg{:d},'
'set_field:{:d}->reg{:d},'
'strip_vlan,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))
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 = {
'table': ovs_consts.TRANSIENT_TABLE,
@ -646,9 +669,20 @@ class TestOVSFirewallDriver(base.BaseTestCase):
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_INGRESS_TABLE)}
ingress_flow_call2 = mock.call(**call_args3)
self.mock_bridge.br.add_flow.assert_has_calls(
[egress_flow_call, ingress_flow_call1, ingress_flow_call2])
expected_calls.append(mock.call(**call_args3))
self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
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):
port_dict = {

View File

@ -286,6 +286,48 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
]
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):
network_type = 'vlan'
vlan_tag = 1111
@ -312,10 +354,36 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
]
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'
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()
expected = [
call._send_msg(ofpp.OFPFlowMod(dp,
@ -488,12 +556,15 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
self.assertEqual(expected, self.mock.mock_calls)
def _test_delete_dvr_dst_mac_for_arp(self, network_type):
if network_type == p_const.TYPE_VLAN:
table_id = constants.DVR_TO_SRC_MAC_VLAN
if network_type in (p_const.TYPE_VLAN, p_const.TYPE_FLAT):
table_id = constants.DVR_TO_SRC_MAC_PHYSICAL
else:
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'
dvr_mac = '00:02:b3:13:fe:3f'
rtr_port = 8888
@ -503,15 +574,26 @@ class OVSIntegrationBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase):
dvr_mac=dvr_mac,
rtr_port=rtr_port)
(dp, ofp, ofpp) = self._get_dp()
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)),
]
if network_type == p_const.TYPE_FLAT:
expected = [
call.uninstall_flows(
strict=True,
priority=5,
table_id=table_id,
match=ofpp.OFPMatch(
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)
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):
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):
test_port = 8888
test_mark = 38

View File

@ -27,7 +27,7 @@ call = mock.call # short hand
class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
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
def setUp(self):
@ -125,10 +125,10 @@ class OVSPhysicalBridgeTest(ovs_bridge_test_base.OVSBridgeTestBase,
]
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'
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()
expected = [
call._send_msg(ofpp.OFPFlowMod(dp,

View File

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