Browse Source

Merge "Make DVR router support FLAT network for ovs-agent" into stable/train

changes/65/759365/1
Zuul 3 months ago
committed by Gerrit Code Review
parent
commit
4bb046c5b8
13 changed files with 300 additions and 139 deletions
  1. +34
    -18
      neutron/agent/linux/openvswitch_firewall/firewall.py
  2. +0
    -3
      neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py
  3. +12
    -9
      neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py
  4. +21
    -16
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py
  5. +4
    -4
      neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py
  6. +21
    -20
      neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py
  7. +5
    -15
      neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py
  8. +2
    -1
      neutron/tests/functional/agent/test_ovs_flows.py
  9. +57
    -23
      neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py
  10. +99
    -14
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py
  11. +3
    -3
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py
  12. +33
    -13
      neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py
  13. +9
    -0
      releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml

+ 34
- 18
neutron/agent/linux/openvswitch_firewall/firewall.py View File

@ -806,32 +806,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
@ -856,8 +871,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,
@ -1441,7 +1457,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)


+ 0
- 3
neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py 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'


+ 12
- 9
neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py 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


+ 21
- 16
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py 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,


+ 4
- 4
neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py 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)

+ 21
- 20
neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py 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(


+ 5
- 15
neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py View File

@ -917,18 +917,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]
@ -1009,7 +999,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 "
@ -1018,8 +1008,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 "


+ 2
- 1
neutron/tests/functional/agent/test_ovs_flows.py 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 +


+ 57
- 23
neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py View File

@ -37,11 +37,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)
@ -630,18 +631,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 = {
@ -656,22 +662,39 @@ class TestOVSFirewallDriver(base.BaseTestCase):
port.vlan_tag,
ovsfw_consts.REG_NET,
ovs_consts.BASE_EGRESS_TABLE)}
egress_flow_call = 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)
expected_calls.append(mock.call(**call_args1))
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,
@ -686,9 +709,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 = {


+ 99
- 14
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py View File

@ -285,6 +285,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
@ -311,10 +353,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,
@ -487,12 +555,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
@ -502,15 +573,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):
@ -518,3 +600,6 @@ 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')

+ 3
- 3
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py 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,


+ 33
- 13
neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py View File

@ -3104,8 +3104,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'
@ -3119,7 +3119,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'))
@ -3282,10 +3283,19 @@ class TestOvsDvrNeutronAgent(object):
self.assertEqual([], phys_br.mock_calls)
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)
@ -3294,10 +3304,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)
@ -3766,8 +3785,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),
@ -3859,7 +3879,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(
@ -3872,7 +3892,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]),
]


+ 9
- 0
releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml 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.

Loading…
Cancel
Save