From cd721a7dcb9073eca2671ffdc9dc7ed9819163ed Mon Sep 17 00:00:00 2001 From: Yang JianFeng Date: Thu, 7 May 2020 05:36:31 +0000 Subject: [PATCH] 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 --- .../linux/openvswitch_firewall/firewall.py | 52 +++++--- .../linuxbridge/agent/common/constants.py | 3 - .../openvswitch/agent/common/constants.py | 21 ++-- .../agent/openflow/native/br_int.py | 37 +++--- .../agent/openflow/native/br_phys.py | 8 +- .../agent/ovs_dvr_neutron_agent.py | 41 +++---- .../openvswitch/agent/ovs_neutron_agent.py | 20 +--- .../tests/functional/agent/test_ovs_flows.py | 3 +- .../openvswitch_firewall/test_firewall.py | 78 ++++++++---- .../agent/openflow/native/test_br_int.py | 113 +++++++++++++++--- .../agent/openflow/native/test_br_phys.py | 6 +- .../agent/test_ovs_neutron_agent.py | 46 +++++-- ...flat-network-for-ovs-fdf8c3eb461426ec.yaml | 9 ++ 13 files changed, 299 insertions(+), 138 deletions(-) create mode 100644 releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml diff --git a/neutron/agent/linux/openvswitch_firewall/firewall.py b/neutron/agent/linux/openvswitch_firewall/firewall.py index bea4f88c593..2dd25f07f68 100644 --- a/neutron/agent/linux/openvswitch_firewall/firewall.py +++ b/neutron/agent/linux/openvswitch_firewall/firewall.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py index e08ee911741..3966fda6398 100644 --- a/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/common/constants.py @@ -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' diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py index 5676698e6a4..060ab85beb1 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/common/constants.py @@ -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 diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py index 515ebd34412..f52a3c2abad 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_int.py @@ -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, diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py index ec8259ee35a..50aa0b73192 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_phys.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py index 2a33ec3db1c..48bb7af123a 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py @@ -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( diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 1ba21a03943..bbabf12339d 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -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 " diff --git a/neutron/tests/functional/agent/test_ovs_flows.py b/neutron/tests/functional/agent/test_ovs_flows.py index e6b29a3d71d..c1fb16c7ead 100644 --- a/neutron/tests/functional/agent/test_ovs_flows.py +++ b/neutron/tests/functional/agent/test_ovs_flows.py @@ -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 + diff --git a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py index fcb1ad44803..c970af3083b 100644 --- a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py +++ b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py @@ -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 = { diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py index ff9a134891c..4ce50c8e06c 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_int.py @@ -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 diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py index 5d473d80ea4..0f41d65cf41 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_phys.py @@ -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, diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index 8c2be7bb39c..731cf7a1a5f 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -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]), ] diff --git a/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml b/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml new file mode 100644 index 00000000000..b106832619a --- /dev/null +++ b/releasenotes/notes/dvr-support-flat-network-for-ovs-fdf8c3eb461426ec.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + ``DVR`` routers now support ``flat`` networks. +fixes: + - | + Fixed bug `1876092 `_ + which caused DUP ICMP replies on the ``flat`` networks used with ``DVR`` + routers.