From e90ea7ce3f1aae1dce216618da49d37a64d3bb91 Mon Sep 17 00:00:00 2001 From: Vivekanandan Narasimhan Date: Sun, 12 Oct 2014 23:15:14 -0700 Subject: [PATCH] VLAN support for DVR Brings in VLAN underlay support for distributed virtual router. In line with it brings in the ability to route packets between VLAN and VXLAN (or) VLAN and GRE networks via distributed virtual routers. Implements: blueprint neutron-ovs-dvr-vlan Closes-Bug: #1374995 Change-Id: If1eb9deef76a49bd0ae0d90ad5ad6a495d97a3b2 --- .../agent/ovs_dvr_neutron_agent.py | 577 +++++++------ .../openvswitch/agent/ovs_neutron_agent.py | 28 +- .../plugins/openvswitch/common/constants.py | 6 + .../openvswitch/test_ovs_dvr_neutron_agent.py | 815 ++++++++++++++++++ .../openvswitch/test_ovs_neutron_agent.py | 593 ------------- 5 files changed, 1160 insertions(+), 859 deletions(-) create mode 100644 neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py diff --git a/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py index 6e2bceb5e..4eeeb04fd 100644 --- a/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py @@ -22,9 +22,9 @@ from neutron.common import constants as n_const from neutron.common import utils as n_utils from neutron.i18n import _LE, _LI, _LW from neutron.openstack.common import log as logging +from neutron.plugins.common import constants as p_const from neutron.plugins.openvswitch.common import constants - LOG = logging.getLogger(__name__) @@ -116,6 +116,7 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # 1.0 Initial version def __init__(self, context, plugin_rpc, integ_br, tun_br, + bridge_mappings, phys_brs, int_ofports, phys_ofports, patch_int_ofport=constants.OFPORT_INVALID, patch_tun_ofport=constants.OFPORT_INVALID, host=None, enable_tunneling=False, @@ -125,11 +126,15 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): self.host = host self.enable_tunneling = enable_tunneling self.enable_distributed_routing = enable_distributed_routing + self.bridge_mappings = bridge_mappings + self.phys_brs = phys_brs + self.int_ofports = int_ofports + self.phys_ofports = phys_ofports self.reset_ovs_parameters(integ_br, tun_br, patch_int_ofport, patch_tun_ofport) self.reset_dvr_parameters() self.dvr_mac_address = None - if self.enable_tunneling and self.enable_distributed_routing: + if self.enable_distributed_routing: self.get_dvr_mac_address() def reset_ovs_parameters(self, integ_br, tun_br, @@ -187,11 +192,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): self.dvr_mac_address = details['mac_address'] return - def setup_dvr_flows_on_integ_tun_br(self): - '''Setup up initial dvr flows into br-int and br-tun''' - if not (self.enable_tunneling and self.enable_distributed_routing): - return - + def setup_dvr_flows_on_integ_br(self): + '''Setup up initial dvr flows into br-int''' if not self.in_distributed_mode(): return @@ -209,38 +211,31 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): priority=1, actions="drop") + self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC_VLAN, + priority=1, + actions="drop") + # Insert 'normal' action as the default for Table LOCAL_SWITCHING self.int_br.add_flow(table=constants.LOCAL_SWITCHING, priority=1, actions="normal") - dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context) - LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs) - for mac in dvr_macs: - if mac['mac_address'] == self.dvr_mac_address: - continue - # Table 0 (default) will now sort DVR traffic from other - # traffic depending on in_port + for physical_network in self.bridge_mappings: self.int_br.add_flow(table=constants.LOCAL_SWITCHING, priority=2, - in_port=self.patch_tun_ofport, - dl_src=mac['mac_address'], - actions="resubmit(,%s)" % - constants.DVR_TO_SRC_MAC) - # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud - # are not learnt, as they may - # result in flow explosions - self.tun_br.add_flow(table=constants.DVR_NOT_LEARN, - priority=1, - dl_src=mac['mac_address'], - actions="output:%s" % self.patch_int_ofport) + in_port=self.int_ofports[physical_network], + actions="drop") - self.registered_dvr_macs.add(mac['mac_address']) + def setup_dvr_flows_on_tun_br(self): + '''Setup up initial dvr flows into br-tun''' + if not self.enable_tunneling or not self.in_distributed_mode(): + return self.tun_br.add_flow(priority=1, in_port=self.patch_int_ofport, actions="resubmit(,%s)" % constants.DVR_PROCESS) + # table-miss should be sent to learning table self.tun_br.add_flow(table=constants.DVR_NOT_LEARN, priority=0, @@ -252,6 +247,77 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): actions="resubmit(,%s)" % constants.PATCH_LV_TO_TUN) + def setup_dvr_flows_on_phys_br(self): + '''Setup up initial dvr flows into br-phys''' + if not self.in_distributed_mode(): + return + + for physical_network in self.bridge_mappings: + self.phys_brs[physical_network].add_flow(priority=2, + in_port=self.phys_ofports[physical_network], + actions="resubmit(,%s)" % + constants.DVR_PROCESS_VLAN) + self.phys_brs[physical_network].add_flow(priority=1, + actions="resubmit(,%s)" % + constants.DVR_NOT_LEARN_VLAN) + self.phys_brs[physical_network].add_flow( + table=constants.DVR_PROCESS_VLAN, + priority=0, + actions="resubmit(,%s)" % + constants.LOCAL_VLAN_TRANSLATION) + self.phys_brs[physical_network].add_flow( + table=constants.LOCAL_VLAN_TRANSLATION, + priority=2, + in_port=self.phys_ofports[physical_network], + actions="drop") + self.phys_brs[physical_network].add_flow( + table=constants.DVR_NOT_LEARN_VLAN, + priority=1, + actions="NORMAL") + + def setup_dvr_mac_flows_on_all_brs(self): + if not self.in_distributed_mode(): + LOG.debug("Not in distributed mode, ignoring invocation " + "of get_dvr_mac_address_list() ") + return + dvr_macs = self.plugin_rpc.get_dvr_mac_address_list(self.context) + LOG.debug("L2 Agent DVR: Received these MACs: %r", dvr_macs) + for mac in dvr_macs: + if mac['mac_address'] == self.dvr_mac_address: + continue + for physical_network in self.bridge_mappings: + self.int_br.add_flow(table=constants.LOCAL_SWITCHING, + priority=4, + in_port=self.int_ofports[physical_network], + dl_src=mac['mac_address'], + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC_VLAN) + self.phys_brs[physical_network].add_flow( + table=constants.DVR_NOT_LEARN_VLAN, + priority=2, + dl_src=mac, + actions="output:%s" % + self.phys_ofports[physical_network]) + + if self.enable_tunneling: + # Table 0 (default) will now sort DVR traffic from other + # traffic depending on in_port + self.int_br.add_flow(table=constants.LOCAL_SWITCHING, + priority=2, + in_port=self.patch_tun_ofport, + dl_src=mac['mac_address'], + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC) + # Table DVR_NOT_LEARN ensures unique dvr macs in the cloud + # are not learnt, as they may + # result in flow explosions + self.tun_br.add_flow(table=constants.DVR_NOT_LEARN, + priority=1, + dl_src=mac, + actions="output:%s" % + self.patch_int_ofport) + self.registered_dvr_macs.add(mac['mac_address']) + def dvr_mac_address_update(self, dvr_macs): if not self.dvr_mac_address: LOG.debug("Self mac unknown, ignoring this " @@ -272,25 +338,48 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): dvr_macs_removed = self.registered_dvr_macs - dvr_host_macs for oldmac in dvr_macs_removed: - self.int_br.delete_flows(table=constants.LOCAL_SWITCHING, - in_port=self.patch_tun_ofport, - dl_src=oldmac) - self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN, - dl_src=oldmac) + for physical_network in self.bridge_mappings: + self.int_br.delete_flows(table=constants.LOCAL_SWITCHING, + in_port=self.int_ofports[physical_network], + dl_src=oldmac) + self.phys_brs[physical_network].delete_flows( + table=constants.DVR_NOT_LEARN_VLAN, + dl_src=oldmac) + if self.enable_tunneling: + self.int_br.delete_flows(table=constants.LOCAL_SWITCHING, + in_port=self.patch_tun_ofport, + dl_src=oldmac) + self.tun_br.delete_flows(table=constants.DVR_NOT_LEARN, + dl_src=oldmac) LOG.debug("Removed DVR MAC flow for %s", oldmac) self.registered_dvr_macs.remove(oldmac) for newmac in dvr_macs_added: - self.int_br.add_flow(table=constants.LOCAL_SWITCHING, - priority=2, - in_port=self.patch_tun_ofport, - dl_src=newmac, - actions="resubmit(,%s)" % - constants.DVR_TO_SRC_MAC) - self.tun_br.add_flow(table=constants.DVR_NOT_LEARN, - priority=1, - dl_src=newmac, - actions="output:%s" % self.patch_int_ofport) + for physical_network in self.bridge_mappings: + self.int_br.add_flow(table=constants.LOCAL_SWITCHING, + priority=4, + in_port=self.int_ofports[physical_network], + dl_src=newmac, + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC_VLAN) + self.phys_brs[physical_network].add_flow( + table=constants.DVR_NOT_LEARN_VLAN, + priority=2, + dl_src=newmac, + actions="output:%s" % + self.phys_ofports[physical_network]) + if self.enable_tunneling: + self.int_br.add_flow(table=constants.LOCAL_SWITCHING, + priority=2, + in_port=self.patch_tun_ofport, + dl_src=newmac, + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC) + self.tun_br.add_flow(table=constants.DVR_NOT_LEARN, + priority=1, + dl_src=newmac, + actions="output:%s" % + self.patch_int_ofport) LOG.debug("Added DVR MAC flow for %s", newmac) self.registered_dvr_macs.add(newmac) @@ -301,9 +390,6 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): return device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE def process_tunneled_network(self, network_type, lvid, segmentation_id): - if not (self.enable_tunneling and self.enable_distributed_routing): - return - if self.in_distributed_mode(): table_id = constants.DVR_NOT_LEARN else: @@ -315,8 +401,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): "resubmit(,%s)" % (lvid, table_id)) - def _bind_distributed_router_interface_port(self, port, fixed_ips, - device_owner, local_vlan): + def _bind_distributed_router_interface_port(self, port, lvm, + fixed_ips, device_owner): # since router port must have only one fixed IP, directly # use fixed_ips[0] subnet_uuid = fixed_ips[0]['subnet_id'] @@ -346,8 +432,14 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # DVR takes over ldm.set_dvr_owned(True) + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if lvm.network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id + subnet_info = ldm.get_subnet_info() - ip_subnet = subnet_info['cidr'] + ip_version = subnet_info['ip_version'] local_compute_ports = ( self.plugin_rpc.get_ports_on_host_by_subnet( self.context, self.host, subnet_uuid)) @@ -363,67 +455,83 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # ensure if a compute port is already on # a different dvr routed subnet # if yes, queue this subnet to that port - ovsport = self.local_ports[vif.vif_id] - ovsport.add_subnet(subnet_uuid) + comp_ovsport = self.local_ports[vif.vif_id] + comp_ovsport.add_subnet(subnet_uuid) else: # the compute port is discovered first here that its on # a dvr routed subnet queue this subnet to that port - ovsport = OVSPort(vif.vif_id, vif.ofport, + comp_ovsport = OVSPort(vif.vif_id, vif.ofport, vif.vif_mac, prt['device_owner']) - - ovsport.add_subnet(subnet_uuid) - self.local_ports[vif.vif_id] = ovsport - + comp_ovsport.add_subnet(subnet_uuid) + self.local_ports[vif.vif_id] = comp_ovsport # create rule for just this vm port - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, + self.int_br.add_flow(table=table_id, priority=4, - dl_vlan=local_vlan, - dl_dst=ovsport.get_mac(), + dl_vlan=vlan_to_use, + dl_dst=comp_ovsport.get_mac(), actions="strip_vlan,mod_dl_src:%s," "output:%s" % (subnet_info['gateway_mac'], - ovsport.get_ofport())) - - # create rule to forward broadcast/multicast frames from dvr - # router interface to appropriate local tenant ports - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) - if csnat_ofport != constants.OFPORT_INVALID: - ofports = str(csnat_ofport) + ',' + ofports - ip_version = subnet_info['ip_version'] - if ofports: - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - - args = {'table': constants.DVR_PROCESS, - 'priority': 3, - 'dl_vlan': local_vlan, - 'actions': "drop"} - if ip_version == 4: - args['proto'] = 'arp' - args['nw_dst'] = subnet_info['gateway_ip'] - else: - args['proto'] = 'icmp6' - args['icmp_type'] = n_const.ICMPV6_TYPE_RA - args['dl_src'] = subnet_info['gateway_mac'] - - self.tun_br.add_flow(**args) - self.tun_br.add_flow(table=constants.DVR_PROCESS, - priority=2, - dl_vlan=local_vlan, - dl_dst=port.vif_mac, - actions="drop") + comp_ovsport.get_ofport())) - self.tun_br.add_flow(table=constants.DVR_PROCESS, - priority=1, - dl_vlan=local_vlan, - dl_src=port.vif_mac, - actions="mod_dl_src:%s,resubmit(,%s)" % - (self.dvr_mac_address, - constants.PATCH_LV_TO_TUN)) + if lvm.network_type == p_const.TYPE_VLAN: + args = {'table': constants.DVR_PROCESS_VLAN, + 'priority': 3, + 'dl_vlan': lvm.vlan, + 'actions': "drop"} + if ip_version == 4: + args['proto'] = 'arp' + args['nw_dst'] = subnet_info['gateway_ip'] + else: + args['proto'] = 'icmp6' + args['icmp_type'] = n_const.ICMPV6_TYPE_RA + args['dl_src'] = subnet_info['gateway_mac'] + # TODO(vivek) remove the IPv6 related add_flow once SNAT is not + # used for IPv6 DVR. + self.phys_brs[lvm.physical_network].add_flow(**args) + self.phys_brs[lvm.physical_network].add_flow( + table=constants.DVR_PROCESS_VLAN, + priority=2, + dl_vlan=lvm.vlan, + dl_dst=port.vif_mac, + actions="drop") + + self.phys_brs[lvm.physical_network].add_flow( + table=constants.DVR_PROCESS_VLAN, + priority=1, + dl_vlan=lvm.vlan, + dl_src=port.vif_mac, + actions="mod_dl_src:%s,resubmit(,%s)" % + (self.dvr_mac_address, constants.LOCAL_VLAN_TRANSLATION)) + + if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: + args = {'table': constants.DVR_PROCESS, + 'priority': 3, + 'dl_vlan': lvm.vlan, + 'actions': "drop"} + if ip_version == 4: + args['proto'] = 'arp' + args['nw_dst'] = subnet_info['gateway_ip'] + else: + args['proto'] = 'icmp6' + args['icmp_type'] = n_const.ICMPV6_TYPE_RA + args['dl_src'] = subnet_info['gateway_mac'] + # TODO(vivek) remove the IPv6 related add_flow once SNAT is not + # used for IPv6 DVR. + self.tun_br.add_flow(**args) + self.tun_br.add_flow(table=constants.DVR_PROCESS, + priority=2, + dl_vlan=lvm.vlan, + dl_dst=port.vif_mac, + actions="drop") + self.tun_br.add_flow(table=constants.DVR_PROCESS, + priority=1, + dl_vlan=lvm.vlan, + dl_src=port.vif_mac, + actions="mod_dl_src:%s,resubmit(,%s)" % + (self.dvr_mac_address, + constants.PATCH_LV_TO_TUN)) # the dvr router interface is itself a port, so capture it # queue this subnet to that port. A subnet appears only once as # a router interface on any given router @@ -432,8 +540,8 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport - def _bind_port_on_dvr_subnet(self, port, fixed_ips, - device_owner, local_vlan): + def _bind_port_on_dvr_subnet(self, port, lvm, fixed_ips, + device_owner): # Handle new compute port added use-case subnet_uuid = None for ips in fixed_ips: @@ -452,8 +560,6 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # the integration bridge LOG.debug("DVR: Plumbing compute port %s", port.vif_id) subnet_info = ldm.get_subnet_info() - ip_subnet = subnet_info['cidr'] - csnat_ofport = ldm.get_csnat_ofport() ldm.add_compute_ofport(port.vif_id, port.ofport) if port.vif_id in self.local_ports: # ensure if a compute port is already on a different @@ -466,31 +572,25 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # on a dvr routed subnet, queue this subnet to that port ovsport = OVSPort(port.vif_id, port.ofport, port.vif_mac, device_owner) - ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if lvm.network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id # create a rule for this vm port - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, + self.int_br.add_flow(table=table_id, priority=4, - dl_vlan=local_vlan, + dl_vlan=vlan_to_use, dl_dst=ovsport.get_mac(), actions="strip_vlan,mod_dl_src:%s," "output:%s" % (subnet_info['gateway_mac'], ovsport.get_ofport())) - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) - if csnat_ofport != constants.OFPORT_INVALID: - ofports = str(csnat_ofport) + ',' + ofports - ip_version = subnet_info['ip_version'] - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - - def _bind_centralized_snat_port_on_dvr_subnet(self, port, fixed_ips, - device_owner, local_vlan): + def _bind_centralized_snat_port_on_dvr_subnet(self, port, lvm, + fixed_ips, device_owner): if port.vif_id in self.local_ports: # throw an error if CSNAT port is already on a different # dvr routed subnet @@ -523,220 +623,178 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): port.vif_mac, device_owner) ovsport.add_subnet(subnet_uuid) self.local_ports[port.vif_id] = ovsport - - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if lvm.network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id + self.int_br.add_flow(table=table_id, priority=4, - dl_vlan=local_vlan, + dl_vlan=vlan_to_use, dl_dst=ovsport.get_mac(), actions="strip_vlan,mod_dl_src:%s," " output:%s" % (subnet_info['gateway_mac'], ovsport.get_ofport())) - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) - ofports = str(ldm.get_csnat_ofport()) + ',' + ofports - ip_subnet = subnet_info['cidr'] - # TODO(xuhanp) remove the IPv6 related add_flow once SNAT is not - # used for IPv6 DVR. - ip_version = subnet_info['ip_version'] - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - - def bind_port_to_dvr(self, port, network_type, fixed_ips, - device_owner, local_vlan_id): + def bind_port_to_dvr(self, port, local_vlan_map, + fixed_ips, device_owner): if not self.in_distributed_mode(): return - if network_type not in constants.TUNNEL_NETWORK_TYPES: + if local_vlan_map.network_type not in (constants.TUNNEL_NETWORK_TYPES + + [p_const.TYPE_VLAN]): + LOG.debug("DVR: Port %s is with network_type %s not supported" + " for dvr plumbing" % (port.vif_id, + local_vlan_map.network_type)) return if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: - self._bind_distributed_router_interface_port(port, fixed_ips, - device_owner, - local_vlan_id) + self._bind_distributed_router_interface_port(port, + local_vlan_map, + fixed_ips, + device_owner) if device_owner and n_utils.is_dvr_serviced(device_owner): - self._bind_port_on_dvr_subnet(port, fixed_ips, - device_owner, - local_vlan_id) + self._bind_port_on_dvr_subnet(port, local_vlan_map, + fixed_ips, + device_owner) if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT: - self._bind_centralized_snat_port_on_dvr_subnet(port, fixed_ips, - device_owner, - local_vlan_id) - - def _unbind_distributed_router_interface_port(self, port, local_vlan): + self._bind_centralized_snat_port_on_dvr_subnet(port, + local_vlan_map, + fixed_ips, + device_owner) + def _unbind_distributed_router_interface_port(self, port, lvm): ovsport = self.local_ports[port.vif_id] - # removal of distributed router interface subnet_ids = ovsport.get_subnets() subnet_set = set(subnet_ids) + network_type = lvm.network_type + physical_network = lvm.physical_network + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id # ensure we process for all the subnets laid on this removed port for sub_uuid in subnet_set: if sub_uuid not in self.local_dvr_map: continue - ldm = self.local_dvr_map[sub_uuid] subnet_info = ldm.get_subnet_info() - ip_subnet = subnet_info['cidr'] - + ip_version = subnet_info['ip_version'] # DVR is no more owner ldm.set_dvr_owned(False) - # remove all vm rules for this dvr subnet # clear of compute_ports altogether compute_ports = ldm.get_compute_ofports() for vif_id in compute_ports: - ovsport = self.local_ports[vif_id] - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - dl_vlan=local_vlan, - dl_dst=ovsport.get_mac()) + comp_port = self.local_ports[vif_id] + self.int_br.delete_flows(table=table_id, + dl_vlan=vlan_to_use, + dl_dst=comp_port.get_mac()) ldm.remove_all_compute_ofports() - ip_version = subnet_info['ip_version'] - if ldm.get_csnat_ofport() != -1: - # If there is a csnat port on this agent, preserve - # the local_dvr_map state - ofports = str(ldm.get_csnat_ofport()) - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - else: - # removed port is a distributed router interface - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, None, None) - self.int_br.delete_flows(**args) - # remove subnet from local_dvr_map as no dvr (or) csnat + if ldm.get_csnat_ofport() == constants.OFPORT_INVALID: + # if there is no csnat port for this subnet, remove + # 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) - - args = {'table': constants.DVR_PROCESS, - 'dl_vlan': local_vlan} - if ip_version == 4: - args['proto'] = 'arp' - args['nw_dst'] = subnet_info['gateway_ip'] - else: - args['proto'] = 'icmp6' - args['icmp_type'] = n_const.ICMPV6_TYPE_RA - args['dl_src'] = subnet_info['gateway_mac'] - - self.tun_br.delete_flows(**args) + if network_type == p_const.TYPE_VLAN: + args = {'table': constants.DVR_PROCESS_VLAN, + 'dl_vlan': lvm.vlan} + if ip_version == 4: + args['proto'] = 'arp' + args['nw_dst'] = subnet_info['gateway_ip'] + else: + args['proto'] = 'icmp6' + args['icmp_type'] = n_const.ICMPV6_TYPE_RA + args['dl_src'] = subnet_info['gateway_mac'] + self.phys_br[physical_network].delete_flows(**args) + + if network_type in constants.TUNNEL_NETWORK_TYPES: + args = {'table': constants.DVR_PROCESS, + 'dl_vlan': lvm.vlan} + if ip_version == 4: + args['proto'] = 'arp' + args['nw_dst'] = subnet_info['gateway_ip'] + else: + args['proto'] = 'icmp6' + args['icmp_type'] = n_const.ICMPV6_TYPE_RA + args['dl_src'] = subnet_info['gateway_mac'] + self.tun_br.delete_flows(**args) ovsport.remove_subnet(sub_uuid) - self.tun_br.delete_flows(table=constants.DVR_PROCESS, - dl_vlan=local_vlan, - dl_dst=port.vif_mac) - - self.tun_br.delete_flows(table=constants.DVR_PROCESS, - dl_vlan=local_vlan, - dl_src=port.vif_mac) + if lvm.network_type == p_const.TYPE_VLAN: + self.phys_br[physical_network].delete_flows( + table=constants.DVR_PROCESS_VLAN, + dl_vlan=lvm.vlan, + dl_dst=port.vif_mac) + self.phys_br[physical_network].delete_flows( + table=constants.DVR_PROCESS_VLAN, + dl_vlan=lvm.vlan, + dl_src=port.vif_mac) + + if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: + self.tun_br.delete_flows(table=constants.DVR_PROCESS, + dl_vlan=lvm.vlan, + dl_dst=port.vif_mac) + self.tun_br.delete_flows(table=constants.DVR_PROCESS, + dl_vlan=lvm.vlan, + dl_src=port.vif_mac) # release port state self.local_ports.pop(port.vif_id, None) - def _unbind_port_on_dvr_subnet(self, port, local_vlan): - + def _unbind_port_on_dvr_subnet(self, port, lvm): ovsport = self.local_ports[port.vif_id] # This confirms that this compute port being removed belonged # to a dvr hosted subnet. - # Accommodate this VM Port into the existing rule in - # the integration bridge LOG.debug("DVR: Removing plumbing for compute port %s", port) subnet_ids = ovsport.get_subnets() # ensure we process for all the subnets laid on this port for sub_uuid in subnet_ids: if sub_uuid not in self.local_dvr_map: continue - ldm = self.local_dvr_map[sub_uuid] - subnet_info = ldm.get_subnet_info() ldm.remove_compute_ofport(port.vif_id) - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) - ip_subnet = subnet_info['cidr'] - ip_version = subnet_info['ip_version'] + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if lvm.network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id # first remove this vm port rule - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - dl_vlan=local_vlan, + self.int_br.delete_flows(table=table_id, + dl_vlan=vlan_to_use, dl_dst=ovsport.get_mac()) - if ldm.get_csnat_ofport() != -1: - # If there is a csnat port on this agent, preserve - # the local_dvr_map state - ofports = str(ldm.get_csnat_ofport()) + ',' + ofports - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - else: - if ofports: - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - else: - # remove the flow altogether, as no ports (both csnat/ - # compute) are available on this subnet in this - # agent - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, None, None) - self.int_br.delete_flows(**args) - # release port state self.local_ports.pop(port.vif_id, None) - def _unbind_centralized_snat_port_on_dvr_subnet(self, port, local_vlan): - + def _unbind_centralized_snat_port_on_dvr_subnet(self, port, lvm): ovsport = self.local_ports[port.vif_id] # This confirms that this compute port being removed belonged # to a dvr hosted subnet. - # Accommodate this VM Port into the existing rule in - # the integration bridge LOG.debug("DVR: Removing plumbing for csnat port %s", port) sub_uuid = list(ovsport.get_subnets())[0] # ensure we process for all the subnets laid on this port if sub_uuid not in self.local_dvr_map: return ldm = self.local_dvr_map[sub_uuid] - subnet_info = ldm.get_subnet_info() - ip_subnet = subnet_info['cidr'] ldm.set_csnat_ofport(constants.OFPORT_INVALID) + table_id = constants.DVR_TO_SRC_MAC + vlan_to_use = lvm.vlan + if lvm.network_type == p_const.TYPE_VLAN: + table_id = constants.DVR_TO_SRC_MAC_VLAN + vlan_to_use = lvm.segmentation_id # then remove csnat port rule - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - dl_vlan=local_vlan, + self.int_br.delete_flows(table=table_id, + dl_vlan=vlan_to_use, dl_dst=ovsport.get_mac()) - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) - ip_version = subnet_info['ip_version'] - if ofports: - # TODO(xuhanp) remove the IPv6 related add_flow once SNAT is not - # used for IPv6 DVR. - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, - 2, ("strip_vlan,mod_dl_src:%s,output:%s" % - (subnet_info['gateway_mac'], ofports))) - self.int_br.add_flow(**args) - else: - args = self._get_flow_args_by_version( - ip_version, constants.DVR_TO_SRC_MAC, - local_vlan, ip_subnet, None, None) - self.int_br.delete_flows(**args) - if not ldm.is_dvr_owned(): # if not owned by DVR (only used for csnat), remove this # subnet state altogether self.local_dvr_map.pop(sub_uuid, None) - # release port state self.local_ports.pop(port.vif_id, None) @@ -761,7 +819,7 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): args['actions'] = actions return args - def unbind_port_from_dvr(self, vif_port, local_vlan_id): + def unbind_port_from_dvr(self, vif_port, local_vlan_map): if not self.in_distributed_mode(): return # Handle port removed use-case @@ -774,11 +832,12 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): if device_owner == n_const.DEVICE_OWNER_DVR_INTERFACE: self._unbind_distributed_router_interface_port(vif_port, - local_vlan_id) + local_vlan_map) if device_owner and n_utils.is_dvr_serviced(device_owner): - self._unbind_port_on_dvr_subnet(vif_port, local_vlan_id) + self._unbind_port_on_dvr_subnet(vif_port, + local_vlan_map) if device_owner == n_const.DEVICE_OWNER_ROUTER_SNAT: self._unbind_centralized_snat_port_on_dvr_subnet(vif_port, - local_vlan_id) + local_vlan_map) diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 54c7061e4..13dae7aaa 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -221,6 +221,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.dvr_plugin_rpc, self.int_br, self.tun_br, + self.bridge_mappings, + self.phys_brs, + self.int_ofports, + self.phys_ofports, self.patch_int_ofport, self.patch_tun_ofport, cfg.CONF.host, @@ -236,7 +240,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if self.enable_tunneling: self.setup_tunnel_br() - self.dvr_agent.setup_dvr_flows_on_integ_tun_br() + self.dvr_agent.setup_dvr_flows_on_integ_br() + self.dvr_agent.setup_dvr_flows_on_tun_br() + self.dvr_agent.setup_dvr_flows_on_phys_br() + self.dvr_agent.setup_dvr_mac_flows_on_all_brs() # Collect additional bridges to monitor self.ancillary_brs = self.setup_ancillary_bridges(integ_br, tun_br) @@ -530,10 +537,18 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if physical_network in self.phys_brs: # outbound br = self.phys_brs[physical_network] - br.add_flow(priority=4, + if self.enable_distributed_routing: + br.add_flow(table=constants.LOCAL_VLAN_TRANSLATION, + priority=4, in_port=self.phys_ofports[physical_network], dl_vlan=lvid, actions="mod_vlan_vid:%s,normal" % segmentation_id) + else: + br.add_flow(priority=4, + in_port=self.phys_ofports[physical_network], + dl_vlan=lvid, + actions="mod_vlan_vid:%s,normal" % segmentation_id) + # inbound self.int_br.add_flow(priority=3, in_port=self. @@ -637,9 +652,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, lvm = self.local_vlan_map[net_uuid] lvm.vif_ports[port.vif_id] = port - self.dvr_agent.bind_port_to_dvr(port, network_type, fixed_ips, - device_owner, - local_vlan_id=lvm.vlan) + self.dvr_agent.bind_port_to_dvr(port, lvm, + fixed_ips, + device_owner) # Do not bind a port if it's already bound cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") @@ -670,8 +685,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if vif_id in lvm.vif_ports: vif_port = lvm.vif_ports[vif_id] - self.dvr_agent.unbind_port_from_dvr(vif_port, - local_vlan_id=lvm.vlan) + self.dvr_agent.unbind_port_from_dvr(vif_port, lvm) lvm.vif_ports.pop(vif_id, None) if not lvm.vif_ports: diff --git a/neutron/plugins/openvswitch/common/constants.py b/neutron/plugins/openvswitch/common/constants.py index 98c122db7..6d84628df 100644 --- a/neutron/plugins/openvswitch/common/constants.py +++ b/neutron/plugins/openvswitch/common/constants.py @@ -37,6 +37,7 @@ TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN] # Various tables for DVR use of integration bridge flows LOCAL_SWITCHING = 0 DVR_TO_SRC_MAC = 1 +DVR_TO_SRC_MAC_VLAN = 2 # Various tables for tunneling flows DVR_PROCESS = 1 @@ -49,6 +50,11 @@ UCAST_TO_TUN = 20 ARP_RESPONDER = 21 FLOOD_TO_TUN = 22 +# Various tables for DVR use of physical bridge flows +DVR_PROCESS_VLAN = 1 +LOCAL_VLAN_TRANSLATION = 2 +DVR_NOT_LEARN_VLAN = 3 + # Tables for integration bridge # Table 0 is used for forwarding. CANARY_TABLE = 23 diff --git a/neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py new file mode 100644 index 000000000..dbef8aa24 --- /dev/null +++ b/neutron/tests/unit/openvswitch/test_ovs_dvr_neutron_agent.py @@ -0,0 +1,815 @@ +# Copyright (c) 2012 OpenStack Foundation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import contextlib + +import mock +from oslo.config import cfg +from oslo import messaging + +from neutron.agent.linux import utils +from neutron.common import constants as n_const +from neutron.plugins.common import constants as p_const +from neutron.plugins.openvswitch.agent import ovs_neutron_agent +from neutron.plugins.openvswitch.common import constants +from neutron.tests import base + + +NOTIFIER = 'neutron.plugins.ml2.rpc.AgentNotifierApi' +OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0" + +FAKE_MAC = '00:11:22:33:44:55' +FAKE_IP1 = '10.0.0.1' +FAKE_IP2 = '10.0.0.2' + + +class CreateAgentConfigMapDvr(base.BaseTestCase): + + def test_create_agent_config_map_enable_distributed_routing(self): + self.addCleanup(cfg.CONF.reset) + # Verify setting only enable_tunneling will default tunnel_type to GRE + cfg.CONF.set_override('enable_distributed_routing', True, + group='AGENT') + cfgmap = ovs_neutron_agent.create_agent_config_map(cfg.CONF) + self.assertEqual(cfgmap['enable_distributed_routing'], True) + + +class TestOvsDvrNeutronAgent(base.BaseTestCase): + + def setUp(self): + super(TestOvsDvrNeutronAgent, self).setUp() + notifier_p = mock.patch(NOTIFIER) + notifier_cls = notifier_p.start() + self.notifier = mock.Mock() + notifier_cls.return_value = self.notifier + cfg.CONF.set_default('firewall_driver', + 'neutron.agent.firewall.NoopFirewallDriver', + group='SECURITYGROUP') + kwargs = ovs_neutron_agent.create_agent_config_map(cfg.CONF) + + class MockFixedIntervalLoopingCall(object): + def __init__(self, f): + self.f = f + + def start(self, interval=0): + self.f() + + with contextlib.nested( + mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.' + 'OVSNeutronAgent.setup_integration_br', + return_value=mock.Mock()), + mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.' + 'OVSNeutronAgent.setup_ancillary_bridges', + return_value=[]), + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'create'), + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_secure_mode'), + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'get_local_port_mac', + return_value='00:00:00:00:00:01'), + mock.patch('neutron.agent.linux.utils.get_interface_mac', + return_value='00:00:00:00:00:01'), + mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges'), + mock.patch('neutron.openstack.common.loopingcall.' + 'FixedIntervalLoopingCall', + new=MockFixedIntervalLoopingCall)): + self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs) + # set back to true because initial report state will succeed due + # to mocked out RPC calls + self.agent.use_call = True + self.agent.tun_br = mock.Mock() + self.agent.sg_agent = mock.Mock() + + def _setup_for_dvr_test(self, ofport=10): + self._port = mock.Mock() + self._port.ofport = ofport + self._port.vif_id = "1234-5678-90" + self._physical_network = 'physeth1' + self._old_local_vlan = None + self._segmentation_id = 2001 + self.agent.enable_distributed_routing = True + self.agent.enable_tunneling = True + self.agent.patch_tun_ofport = 1 + self.agent.patch_int_ofport = 2 + self.agent.dvr_agent.local_ports = {} + self.agent.local_vlan_map = {} + self.agent.dvr_agent.enable_distributed_routing = True + self.agent.dvr_agent.enable_tunneling = True + self.agent.dvr_agent.patch_tun_ofport = 1 + self.agent.dvr_agent.patch_int_ofport = 2 + self.agent.dvr_agent.tun_br = mock.Mock() + self.agent.dvr_agent.phys_brs[self._physical_network] = mock.Mock() + self.agent.dvr_agent.bridge_mappings = {self._physical_network: + 'br-eth1'} + self.agent.dvr_agent.int_ofports[self._physical_network] = 30 + self.agent.dvr_agent.phys_ofports[self._physical_network] = 40 + self.agent.dvr_agent.local_dvr_map = {} + self.agent.dvr_agent.registered_dvr_macs = set() + self.agent.dvr_agent.dvr_mac_address = 'aa:22:33:44:55:66' + self._net_uuid = 'my-net-uuid' + self._fixed_ips = [{'subnet_id': 'my-subnet-uuid', + 'ip_address': '1.1.1.1'}] + self._compute_port = mock.Mock() + self._compute_port.ofport = 20 + self._compute_port.vif_id = "1234-5678-91" + self._compute_fixed_ips = [{'subnet_id': 'my-subnet-uuid', + 'ip_address': '1.1.1.3'}] + + def _test_port_bound_for_dvr_on_vlan_network(self, device_owner, + ip_version=4): + self._setup_for_dvr_test() + if ip_version == 4: + gateway_ip = '1.1.1.1' + cidr = '1.1.1.0/24' + else: + gateway_ip = '2001:100::1' + cidr = '2001:100::0/64' + self._port.vif_mac = gateway_mac = 'aa:bb:cc:11:22:33' + self._compute_port.vif_mac = '77:88:99:00:11:22' + physical_network = self._physical_network + segmentation_id = self._segmentation_id + network_type = p_const.TYPE_VLAN + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=str(self._old_local_vlan)), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_subnet_for_dvr', + return_value={ + 'gateway_ip': gateway_ip, + 'cidr': cidr, + 'ip_version': ip_version, + 'gateway_mac': gateway_mac}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'), + mock.patch.object( + self.agent.dvr_agent.phys_brs[physical_network], + 'add_flow'), + mock.patch.object( + self.agent.dvr_agent.phys_brs[physical_network], + 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn, add_flow_phys_fn, + delete_flows_phys_fn): + self.agent.port_bound( + self._port, self._net_uuid, network_type, + physical_network, segmentation_id, self._fixed_ips, + n_const.DEVICE_OWNER_DVR_INTERFACE, False) + lvm = self.agent.local_vlan_map[self._net_uuid] + phy_ofp = self.agent.dvr_agent.phys_ofports[physical_network] + int_ofp = self.agent.dvr_agent.int_ofports[physical_network] + expected_on_phys_br = [ + mock.call(table=constants.LOCAL_VLAN_TRANSLATION, + priority=4, + in_port=phy_ofp, + dl_vlan=lvm.vlan, + actions="mod_vlan_vid:%s,normal" % + (lvm.segmentation_id)), + mock.call(table=constants.DVR_PROCESS_VLAN, + priority=2, + dl_vlan=lvm.vlan, + dl_dst=self._port.vif_mac, + actions="drop"), + mock.call(table=constants.DVR_PROCESS_VLAN, + priority=1, + dl_vlan=lvm.vlan, + dl_src=self._port.vif_mac, + actions="mod_dl_src:%s,resubmit(,%s)" % + (self.agent.dvr_agent.dvr_mac_address, + constants.LOCAL_VLAN_TRANSLATION)) + ] + if ip_version == 4: + expected_on_phys_br.insert(1, mock.call( + proto='arp', + nw_dst=gateway_ip, actions='drop', + priority=3, table=constants.DVR_PROCESS_VLAN, + dl_vlan=lvm.vlan)) + else: + expected_on_phys_br.insert(1, mock.call( + icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6', + dl_src=self._port.vif_mac, actions='drop', + priority=3, table=constants.DVR_PROCESS_VLAN, + dl_vlan=lvm.vlan)) + self.assertEqual(expected_on_phys_br, + add_flow_phys_fn.call_args_list) + self.agent.port_bound(self._compute_port, self._net_uuid, + network_type, physical_network, + segmentation_id, + self._compute_fixed_ips, + device_owner, False) + expected_on_int_br = [ + mock.call(priority=3, + in_port=int_ofp, + dl_vlan=lvm.segmentation_id, + actions="mod_vlan_vid:%s,normal" % lvm.vlan), + mock.call(table=constants.DVR_TO_SRC_MAC_VLAN, + priority=4, + dl_dst=self._compute_port.vif_mac, + dl_vlan=lvm.segmentation_id, + actions="strip_vlan,mod_dl_src:%s," + "output:%s" % + (gateway_mac, + self._compute_port.ofport)) + ] + self.assertEqual(expected_on_int_br, + add_flow_int_fn.call_args_list) + expected_on_int_br = [ + mock.call(in_port=self._port.ofport), + mock.call(in_port=self._compute_port.ofport) + ] + self.assertEqual(expected_on_int_br, + delete_flows_int_fn.call_args_list) + self.assertFalse(add_flow_tun_fn.called) + self.assertFalse(delete_flows_tun_fn.called) + self.assertFalse(delete_flows_phys_fn.called) + + def _test_port_bound_for_dvr_on_vxlan_network(self, device_owner, + ip_version=4): + self._setup_for_dvr_test() + if ip_version == 4: + gateway_ip = '1.1.1.1' + cidr = '1.1.1.0/24' + else: + gateway_ip = '2001:100::1' + cidr = '2001:100::0/64' + network_type = p_const.TYPE_VXLAN + self._port.vif_mac = gateway_mac = 'aa:bb:cc:11:22:33' + self._compute_port.vif_mac = '77:88:99:00:11:22' + physical_network = self._physical_network + segmentation_id = self._segmentation_id + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=self._old_local_vlan), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_subnet_for_dvr', + return_value={ + 'gateway_ip': gateway_ip, + 'cidr': cidr, + 'ip_version': ip_version, + 'gateway_mac': gateway_mac}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'), + mock.patch.object( + self.agent.dvr_agent.phys_brs[physical_network], + 'add_flow'), + mock.patch.object( + self.agent.dvr_agent.phys_brs[physical_network], + 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn, + add_flow_phys_fn, delete_flows_phys_fn): + self.agent.port_bound( + self._port, self._net_uuid, network_type, + physical_network, segmentation_id, self._fixed_ips, + n_const.DEVICE_OWNER_DVR_INTERFACE, False) + lvm = self.agent.local_vlan_map[self._net_uuid] + expected_in_tun_br = [ + mock.call( + table=constants.TUN_TABLE['vxlan'], + priority=1, tun_id=lvm.segmentation_id, + actions="mod_vlan_vid:%s," + "resubmit(,%s)" % + (lvm.vlan, constants.DVR_NOT_LEARN)), + mock.call( + table=constants.DVR_PROCESS, priority=2, + dl_vlan=lvm.vlan, + dl_dst=self._port.vif_mac, + actions='drop'), + mock.call( + table=constants.DVR_PROCESS, priority=1, + dl_vlan=lvm.vlan, + dl_src=self._port.vif_mac, + actions="mod_dl_src:%s,resubmit(,%s)" % ( + self.agent.dvr_agent.dvr_mac_address, + constants.PATCH_LV_TO_TUN))] + if ip_version == 4: + expected_in_tun_br.insert(1, mock.call( + proto='arp', + nw_dst=gateway_ip, actions='drop', + priority=3, table=constants.DVR_PROCESS, + dl_vlan=lvm.vlan)) + else: + expected_in_tun_br.insert(1, mock.call( + icmp_type=n_const.ICMPV6_TYPE_RA, + proto='icmp6', + dl_src=self._port.vif_mac, + actions='drop', + priority=3, table=constants.DVR_PROCESS, + dl_vlan=lvm.vlan)) + self.assertEqual(expected_in_tun_br, + add_flow_tun_fn.call_args_list) + self.agent.port_bound(self._compute_port, self._net_uuid, + network_type, physical_network, + segmentation_id, + self._compute_fixed_ips, + device_owner, False) + expected_in_int_br = [ + mock.call(table=constants.DVR_TO_SRC_MAC, priority=4, + dl_dst=self._compute_port.vif_mac, + dl_vlan=lvm.vlan, + actions="strip_vlan,mod_dl_src:%s," + "output:%s" % + (gateway_mac, self._compute_port.ofport)) + ] + self.assertEqual(expected_in_int_br, + add_flow_int_fn.call_args_list) + self.assertFalse(add_flow_phys_fn.called) + expected_in_int_br = [ + mock.call(in_port=self._port.ofport), + mock.call(in_port=self._compute_port.ofport) + ] + self.assertEqual(expected_in_int_br, + delete_flows_int_fn.call_args_list) + self.assertFalse(add_flow_phys_fn.called) + self.assertFalse(delete_flows_tun_fn.called) + self.assertFalse(delete_flows_phys_fn.called) + + def test_port_bound_for_dvr_with_compute_ports(self): + self._test_port_bound_for_dvr_on_vlan_network( + device_owner="compute:None") + self._test_port_bound_for_dvr_on_vlan_network( + device_owner="compute:None", ip_version=6) + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner="compute:None") + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner="compute:None", ip_version=6) + + def test_port_bound_for_dvr_with_lbaas_vip_ports(self): + self._test_port_bound_for_dvr_on_vlan_network( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER) + self._test_port_bound_for_dvr_on_vlan_network( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER) + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER, 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( + device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner=n_const.DEVICE_OWNER_DHCP) + self._test_port_bound_for_dvr_on_vxlan_network( + device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) + + def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10): + self._setup_for_dvr_test() + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=self._old_local_vlan), + mock.patch.object( + self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', + return_value={'gateway_ip': '1.1.1.1', + 'cidr': '1.1.1.0/24', + 'ip_version': 4, + 'gateway_mac': 'aa:bb:cc:11:22:33'}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn): + self.agent.port_bound( + self._port, self._net_uuid, 'vxlan', + None, None, self._fixed_ips, + n_const.DEVICE_OWNER_ROUTER_SNAT, + False) + self.assertTrue(add_flow_int_fn.called) + self.assertTrue(delete_flows_int_fn.called) + + def test_treat_devices_removed_for_dvr_interface(self, ofport=10): + self._test_treat_devices_removed_for_dvr_interface(ofport) + self._test_treat_devices_removed_for_dvr_interface( + ofport, ip_version=6) + + def _test_treat_devices_removed_for_dvr_interface(self, ofport=10, + ip_version=4): + self._setup_for_dvr_test() + if ip_version == 4: + gateway_ip = '1.1.1.1' + cidr = '1.1.1.0/24' + else: + gateway_ip = '2001:100::1' + cidr = '2001:100::0/64' + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=self._old_local_vlan), + mock.patch.object( + self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', + return_value={'gateway_ip': gateway_ip, + 'cidr': cidr, + 'ip_version': ip_version, + 'gateway_mac': 'aa:bb:cc:11:22:33'}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn): + self.agent.port_bound( + self._port, self._net_uuid, 'vxlan', + None, None, self._fixed_ips, + n_const.DEVICE_OWNER_DVR_INTERFACE, + False) + self.assertTrue(add_flow_tun_fn.called) + self.assertTrue(delete_flows_int_fn.called) + + with contextlib.nested( + mock.patch.object(self.agent, 'reclaim_local_vlan'), + mock.patch.object(self.agent.plugin_rpc, 'update_device_down', + return_value=None), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, + 'delete_flows')) as (reclaim_vlan_fn, + update_dev_down_fn, + delete_flows_int_fn, + delete_flows_tun_fn): + self.agent.treat_devices_removed([self._port.vif_id]) + if ip_version == 4: + expected = [mock.call( + proto='arp', + nw_dst=gateway_ip, + table=constants.DVR_PROCESS, + dl_vlan=( + self.agent.local_vlan_map[self._net_uuid].vlan))] + else: + expected = [mock.call( + icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6', + dl_src='aa:bb:cc:11:22:33', + table=constants.DVR_PROCESS, + dl_vlan=( + self.agent.local_vlan_map[self._net_uuid].vlan))] + expected.extend([ + mock.call( + table=constants.DVR_PROCESS, + dl_dst=self._port.vif_mac, + dl_vlan=( + self.agent.local_vlan_map[self._net_uuid].vlan)), + mock.call( + table=constants.DVR_PROCESS, + dl_vlan=( + self.agent.local_vlan_map[self._net_uuid].vlan), + dl_src=self._port.vif_mac) + ]) + self.assertEqual(expected, delete_flows_tun_fn.call_args_list) + + def _test_treat_devices_removed_for_dvr(self, device_owner, ip_version=4): + self._setup_for_dvr_test() + if ip_version == 4: + gateway_ip = '1.1.1.1' + cidr = '1.1.1.0/24' + else: + gateway_ip = '2001:100::1' + cidr = '2001:100::0/64' + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=self._old_local_vlan), + mock.patch.object( + self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', + return_value={'gateway_ip': gateway_ip, + 'cidr': cidr, + 'ip_version': ip_version, + 'gateway_mac': 'aa:bb:cc:11:22:33'}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn): + self.agent.port_bound( + self._port, self._net_uuid, 'vxlan', + None, None, self._fixed_ips, + n_const.DEVICE_OWNER_DVR_INTERFACE, + False) + self.agent.port_bound(self._compute_port, + self._net_uuid, 'vxlan', + None, None, + self._compute_fixed_ips, + device_owner, False) + self.assertTrue(add_flow_tun_fn.called) + self.assertTrue(add_flow_int_fn.called) + self.assertTrue(delete_flows_int_fn.called) + + with contextlib.nested( + mock.patch.object(self.agent, 'reclaim_local_vlan'), + mock.patch.object(self.agent.plugin_rpc, 'update_device_down', + return_value=None), + mock.patch.object(self.agent.dvr_agent.int_br, + 'delete_flows')) as (reclaim_vlan_fn, + update_dev_down_fn, + delete_flows_int_fn): + self.agent.treat_devices_removed([self._compute_port.vif_id]) + expected = [ + mock.call( + table=constants.DVR_TO_SRC_MAC, + dl_dst=self._compute_port.vif_mac, + dl_vlan=( + self.agent.local_vlan_map[self._net_uuid].vlan))] + self.assertEqual(expected, delete_flows_int_fn.call_args_list) + + def test_treat_devices_removed_for_dvr_with_compute_ports(self): + self._test_treat_devices_removed_for_dvr( + device_owner="compute:None") + self._test_treat_devices_removed_for_dvr( + device_owner="compute:None", ip_version=6) + + def test_treat_devices_removed_for_dvr_with_lbaas_vip_ports(self): + self._test_treat_devices_removed_for_dvr( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER) + self._test_treat_devices_removed_for_dvr( + device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) + + def test_treat_devices_removed_for_dvr_with_dhcp_ports(self): + self._test_treat_devices_removed_for_dvr( + device_owner=n_const.DEVICE_OWNER_DHCP) + self._test_treat_devices_removed_for_dvr( + device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) + + def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10): + self._setup_for_dvr_test() + with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'set_db_attribute', + return_value=True): + with contextlib.nested( + mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' + 'db_get_val', + return_value=self._old_local_vlan), + mock.patch.object( + self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', + return_value={'gateway_ip': '1.1.1.1', + 'cidr': '1.1.1.0/24', + 'ip_version': 4, + 'gateway_mac': 'aa:bb:cc:11:22:33'}), + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_ports_on_host_by_subnet', + return_value=[]), + mock.patch.object(self.agent.dvr_agent.int_br, + 'get_vif_port_by_id', + return_value=self._port), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') + ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, delete_flows_int_fn, + add_flow_tun_fn, delete_flows_tun_fn): + self.agent.port_bound( + self._port, self._net_uuid, 'vxlan', + None, None, self._fixed_ips, + n_const.DEVICE_OWNER_ROUTER_SNAT, + False) + self.assertTrue(add_flow_int_fn.called) + self.assertTrue(delete_flows_int_fn.called) + + with contextlib.nested( + mock.patch.object(self.agent, 'reclaim_local_vlan'), + mock.patch.object(self.agent.plugin_rpc, 'update_device_down', + return_value=None), + mock.patch.object(self.agent.dvr_agent.int_br, + 'delete_flows')) as (reclaim_vlan_fn, + update_dev_down_fn, + delete_flows_int_fn): + self.agent.treat_devices_removed([self._port.vif_id]) + self.assertTrue(delete_flows_int_fn.called) + + def test_setup_dvr_flows_on_int_br(self): + self._setup_for_dvr_test() + with contextlib.nested( + mock.patch.object(self.agent.dvr_agent.int_br, + 'remove_all_flows'), + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object( + self.agent.dvr_agent.plugin_rpc, + 'get_dvr_mac_address_list', + return_value=[{'host': 'cn1', + 'mac_address': 'aa:bb:cc:dd:ee:ff'}, + {'host': 'cn2', + 'mac_address': '11:22:33:44:55:66'}])) as \ + (remove_flows_fn, add_int_flow_fn, add_tun_flow_fn, + get_mac_list_fn): + self.agent.dvr_agent.setup_dvr_flows_on_integ_br() + self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) + physical_networks = self.agent.dvr_agent.bridge_mappings.keys() + ioport = self.agent.dvr_agent.int_ofports[physical_networks[0]] + expected = [ + mock.call(table=constants.CANARY_TABLE, + priority=0, + actions="drop"), + mock.call(table=constants.DVR_TO_SRC_MAC, + priority=1, + actions="drop"), + mock.call(table=constants.DVR_TO_SRC_MAC_VLAN, + priority=1, + actions="drop"), + mock.call(table=constants.LOCAL_SWITCHING, + priority=1, + actions="normal"), + mock.call( + table=constants.LOCAL_SWITCHING, priority=2, + actions="drop", + in_port=ioport)] + self.assertTrue(remove_flows_fn.called) + self.assertEqual(expected, add_int_flow_fn.call_args_list) + self.assertEqual(add_int_flow_fn.call_count, 5) + + def test_get_dvr_mac_address(self): + self._setup_for_dvr_test() + self.agent.dvr_agent.dvr_mac_address = None + with mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_dvr_mac_address_by_host', + return_value={'host': 'cn1', + 'mac_address': 'aa:22:33:44:55:66'}): + self.agent.dvr_agent.get_dvr_mac_address() + self.assertEqual('aa:22:33:44:55:66', + self.agent.dvr_agent.dvr_mac_address) + self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) + + def test_get_dvr_mac_address_exception(self): + self._setup_for_dvr_test() + self.agent.dvr_agent.dvr_mac_address = None + with contextlib.nested( + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_dvr_mac_address_by_host', + side_effect=messaging.RemoteError), + mock.patch.object(self.agent.dvr_agent.int_br, + 'add_flow')) as (gd_mac, add_int_flow_fn): + + self.agent.dvr_agent.get_dvr_mac_address() + self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) + self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) + self.assertEqual(add_int_flow_fn.call_count, 1) + + def test_get_dvr_mac_address_retried(self): + valid_entry = {'host': 'cn1', 'mac_address': 'aa:22:33:44:55:66'} + raise_timeout = messaging.MessagingTimeout() + # Raise a timeout the first 2 times it calls get_dvr_mac_address() + self._setup_for_dvr_test() + self.agent.dvr_agent.dvr_mac_address = None + with mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_dvr_mac_address_by_host', + side_effect=(raise_timeout, raise_timeout, + valid_entry)): + self.agent.dvr_agent.get_dvr_mac_address() + self.assertEqual('aa:22:33:44:55:66', + self.agent.dvr_agent.dvr_mac_address) + self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) + self.assertEqual(self.agent.dvr_agent.plugin_rpc. + get_dvr_mac_address_by_host.call_count, 3) + + def test_get_dvr_mac_address_retried_max(self): + raise_timeout = messaging.MessagingTimeout() + # Raise a timeout every time until we give up, currently 5 tries + self._setup_for_dvr_test() + self.agent.dvr_agent.dvr_mac_address = None + with contextlib.nested( + mock.patch.object(self.agent.dvr_agent.plugin_rpc, + 'get_dvr_mac_address_by_host', + side_effect=raise_timeout), + mock.patch.object(utils, "execute"), + ) as (rpc_mock, execute_mock): + self.agent.dvr_agent.get_dvr_mac_address() + self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) + self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) + self.assertEqual(self.agent.dvr_agent.plugin_rpc. + get_dvr_mac_address_by_host.call_count, 5) + + def test_dvr_mac_address_update(self): + self._setup_for_dvr_test() + newhost = 'cn2' + newmac = 'aa:bb:cc:dd:ee:ff' + int_ofport = self.agent.dvr_agent.int_ofports['physeth1'] + patch_int_ofport = self.agent.dvr_agent.patch_int_ofport + patch_tun_ofport = self.agent.dvr_agent.patch_tun_ofport + with contextlib.nested( + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.phys_brs['physeth1'], + 'add_flow') + ) as (add_flow_fn, add_flow_tn_fn, del_flows_fn, add_flow_phys_fn): + self.agent.dvr_agent.\ + dvr_mac_address_update( + dvr_macs=[{'host': newhost, + 'mac_address': newmac}]) + expected = [ + mock.call(table=constants.LOCAL_SWITCHING, + priority=4, + in_port=int_ofport, + dl_src=newmac, + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC_VLAN), + mock.call(table=constants.LOCAL_SWITCHING, + priority=2, + in_port=patch_tun_ofport, + dl_src=newmac, + actions="resubmit(,%s)" % + constants.DVR_TO_SRC_MAC)] + self.assertEqual(expected, add_flow_fn.call_args_list) + add_flow_phys_fn.assert_called_with( + table=constants.DVR_NOT_LEARN_VLAN, + priority=2, + dl_src=newmac, + actions="output:%s" % + self.agent.dvr_agent.phys_ofports['physeth1']) + add_flow_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN, + priority=1, + dl_src=newmac, + actions="output:%s" + % patch_int_ofport) + self.assertFalse(del_flows_fn.called) + with contextlib.nested( + mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), + mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), + mock.patch.object(self.agent.dvr_agent.phys_brs['physeth1'], + 'delete_flows'), + ) as (add_flow_fn, del_flows_tn_fn, del_flows_fn, del_flows_phys_fn): + self.agent.dvr_agent.dvr_mac_address_update(dvr_macs=[]) + ioport = self.agent.dvr_agent.int_ofports['physeth1'] + expected = [ + mock.call(table=constants.LOCAL_SWITCHING, + in_port=ioport, + dl_src=newmac), + mock.call(table=constants.LOCAL_SWITCHING, + in_port=patch_tun_ofport, + dl_src=newmac)] + self.assertEqual(expected, del_flows_fn.call_args_list) + del_flows_phys_fn.asert_called_with( + table=constants.DVR_NOT_LEARN_VLAN, + dl_src=newmac) + del_flows_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN, + dl_src=newmac) + self.assertFalse(add_flow_fn.called) diff --git a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py index baa71b1a6..dad616709 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py @@ -19,7 +19,6 @@ import time import mock import netaddr from oslo.config import cfg -from oslo import messaging import testtools from neutron.agent.linux import async_process @@ -168,34 +167,6 @@ class TestOvsNeutronAgent(base.BaseTestCase): self.assertFalse(set_ovs_db_func.called) self.assertFalse(delete_flows_func.called) - def _setup_for_dvr_test(self, ofport=10): - self._port = mock.Mock() - self._port.ofport = ofport - self._port.vif_id = "1234-5678-90" - self.agent.enable_distributed_routing = True - self.agent.enable_tunneling = True - self.agent.patch_tun_ofport = 1 - self.agent.patch_int_ofport = 2 - self.agent.dvr_agent.local_ports = {} - self.agent.local_vlan_map = {} - self.agent.dvr_agent.enable_distributed_routing = True - self.agent.dvr_agent.enable_tunneling = True - self.agent.dvr_agent.patch_tun_ofport = 1 - self.agent.dvr_agent.patch_int_ofport = 2 - self.agent.dvr_agent.tun_br = mock.Mock() - self.agent.dvr_agent.local_dvr_map = {} - self.agent.dvr_agent.registered_dvr_macs = set() - self.agent.dvr_agent.dvr_mac_address = 'aa:22:33:44:55:66' - self._net_uuid = 'my-net-uuid' - self._fixed_ips = [{'subnet_id': 'my-subnet-uuid', - 'ip_address': '1.1.1.1'}] - self._compute_port = mock.Mock() - self._compute_port.ofport = 20 - self._compute_port.vif_id = "1234-5678-91" - self._old_local_vlan = None - self._compute_fixed_ips = [{'subnet_id': 'my-subnet-uuid', - 'ip_address': '1.1.1.3'}] - def test_port_bound_deletes_flows_for_valid_ofport(self): self._mock_port_bound(ofport=1, new_local_vlan=1) @@ -205,541 +176,6 @@ class TestOvsNeutronAgent(base.BaseTestCase): def test_port_bound_does_not_rewire_if_already_bound(self): self._mock_port_bound(ofport=-1, new_local_vlan=1, old_local_vlan=1) - def test_port_bound_for_dvr_interface(self, ofport=10): - self._setup_for_dvr_test() - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', - return_value={'gateway_ip': '1.1.1.1', - 'cidr': '1.1.1.0/24', - 'ip_version': 4, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_DVR_INTERFACE, - False) - self.assertTrue(add_flow_tun_fn.called) - self.assertTrue(delete_flows_int_fn.called) - - def _test_port_bound_for_dvr(self, device_owner, ip_version=4): - self._setup_for_dvr_test() - if ip_version == 4: - gateway_ip = '1.1.1.1' - cidr = '1.1.1.0/24' - else: - gateway_ip = '2001:100::1' - cidr = '2001:100::0/64' - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_subnet_for_dvr', - return_value={ - 'gateway_ip': gateway_ip, - 'cidr': cidr, - 'ip_version': ip_version, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_DVR_INTERFACE, - False) - expected = [ - mock.call( - table=constants.TUN_TABLE['vxlan'], - priority=1, tun_id=None, - actions="mod_vlan_vid:%s," - "resubmit(,%s)" % - (self.agent.local_vlan_map[self._net_uuid].vlan, - constants.DVR_NOT_LEARN)), - mock.call( - table=constants.DVR_PROCESS, priority=2, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - dl_dst=self._port.vif_mac, - actions='drop'), - mock.call( - table=constants.DVR_PROCESS, priority=1, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - dl_src=self._port.vif_mac, - actions="mod_dl_src:%s,resubmit(,%s)" % ( - self.agent.dvr_agent.dvr_mac_address, - constants.PATCH_LV_TO_TUN))] - if ip_version == 4: - expected.insert(1, mock.call( - proto='arp', - nw_dst=gateway_ip, actions='drop', - priority=3, table=constants.DVR_PROCESS, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan))) - else: - expected.insert(1, mock.call( - icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6', - dl_src='aa:bb:cc:11:22:33', actions='drop', - priority=3, table=constants.DVR_PROCESS, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan))) - - self.assertEqual(expected, add_flow_tun_fn.call_args_list) - self.agent.port_bound(self._compute_port, self._net_uuid, - 'vxlan', None, None, - self._compute_fixed_ips, - device_owner, False) - expected = [ - mock.call(table=constants.DVR_TO_SRC_MAC, priority=4, - dl_dst=self._compute_port.vif_mac, - dl_vlan=self.agent.local_vlan_map[self._net_uuid].vlan, - actions="strip_vlan,mod_dl_src:%s," - "output:%s" % - ('aa:bb:cc:11:22:33', self._compute_port.ofport)) - ] - if ip_version == 4: - expected.append(mock.call( - table=constants.DVR_TO_SRC_MAC, - priority=2, proto='ip', - nw_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - actions="strip_vlan,mod_dl_src:%s," - "output:%s" % - ('aa:bb:cc:11:22:33', self._compute_port.ofport))) - else: - expected.append(mock.call( - table=constants.DVR_TO_SRC_MAC, - priority=2, proto='ipv6', - ipv6_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - actions="strip_vlan,mod_dl_src:%s," - "output:%s" % - ('aa:bb:cc:11:22:33', self._compute_port.ofport))) - self.assertEqual(expected, add_flow_int_fn.call_args_list) - self.assertTrue(delete_flows_int_fn.called) - - def test_port_bound_for_dvr_with_compute_ports(self): - self._test_port_bound_for_dvr( - device_owner="compute:None") - self._test_port_bound_for_dvr( - device_owner="compute:None", ip_version=6) - - def test_port_bound_for_dvr_with_lbaas_vip_ports(self): - self._test_port_bound_for_dvr( - device_owner=n_const.DEVICE_OWNER_LOADBALANCER) - self._test_port_bound_for_dvr( - device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) - - def test_port_bound_for_dvr_with_dhcp_ports(self): - self._test_port_bound_for_dvr( - device_owner=n_const.DEVICE_OWNER_DHCP) - self._test_port_bound_for_dvr( - device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) - - def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10): - self._setup_for_dvr_test() - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', - return_value={'gateway_ip': '1.1.1.1', - 'cidr': '1.1.1.0/24', - 'ip_version': 4, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_ROUTER_SNAT, - False) - self.assertTrue(add_flow_int_fn.called) - self.assertTrue(delete_flows_int_fn.called) - - def test_treat_devices_removed_for_dvr_interface(self, ofport=10): - self._test_treat_devices_removed_for_dvr_interface(ofport) - self._test_treat_devices_removed_for_dvr_interface( - ofport, ip_version=6) - - def _test_treat_devices_removed_for_dvr_interface(self, ofport=10, - ip_version=4): - self._setup_for_dvr_test() - if ip_version == 4: - gateway_ip = '1.1.1.1' - cidr = '1.1.1.0/24' - else: - gateway_ip = '2001:100::1' - cidr = '2001:100::0/64' - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', - return_value={'gateway_ip': gateway_ip, - 'cidr': cidr, - 'ip_version': ip_version, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_DVR_INTERFACE, - False) - self.assertTrue(add_flow_tun_fn.called) - self.assertTrue(delete_flows_int_fn.called) - - with contextlib.nested( - mock.patch.object(self.agent, 'reclaim_local_vlan'), - mock.patch.object(self.agent.plugin_rpc, 'update_device_down', - return_value=None), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, - 'delete_flows')) as (reclaim_vlan_fn, - update_dev_down_fn, - delete_flows_int_fn, - delete_flows_tun_fn): - self.agent.treat_devices_removed([self._port.vif_id]) - if ip_version == 4: - expected = [mock.call( - table=constants.DVR_TO_SRC_MAC, - nw_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - proto='ip')] - else: - expected = [mock.call( - table=constants.DVR_TO_SRC_MAC, - ipv6_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - proto='ipv6')] - self.assertEqual(expected, - delete_flows_int_fn.call_args_list) - if ip_version == 4: - expected = [mock.call( - proto='arp', - nw_dst=gateway_ip, - table=constants.DVR_PROCESS, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan))] - else: - expected = [mock.call( - icmp_type=n_const.ICMPV6_TYPE_RA, proto='icmp6', - dl_src='aa:bb:cc:11:22:33', - table=constants.DVR_PROCESS, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan))] - expected.extend([ - mock.call( - table=constants.DVR_PROCESS, - dl_dst=self._port.vif_mac, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan)), - mock.call( - table=constants.DVR_PROCESS, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan), - dl_src=self._port.vif_mac) - ]) - self.assertEqual(expected, delete_flows_tun_fn.call_args_list) - - def _test_treat_devices_removed_for_dvr(self, device_owner, ip_version=4): - self._setup_for_dvr_test() - if ip_version == 4: - gateway_ip = '1.1.1.1' - cidr = '1.1.1.0/24' - else: - gateway_ip = '2001:100::1' - cidr = '2001:100::0/64' - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', - return_value={'gateway_ip': gateway_ip, - 'cidr': cidr, - 'ip_version': ip_version, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_DVR_INTERFACE, - False) - self.agent.port_bound(self._compute_port, - self._net_uuid, 'vxlan', - None, None, - self._compute_fixed_ips, - device_owner, False) - self.assertTrue(add_flow_tun_fn.called) - self.assertTrue(add_flow_int_fn.called) - self.assertTrue(delete_flows_int_fn.called) - - with contextlib.nested( - mock.patch.object(self.agent, 'reclaim_local_vlan'), - mock.patch.object(self.agent.plugin_rpc, 'update_device_down', - return_value=None), - mock.patch.object(self.agent.dvr_agent.int_br, - 'delete_flows')) as (reclaim_vlan_fn, - update_dev_down_fn, - delete_flows_int_fn): - self.agent.treat_devices_removed([self._compute_port.vif_id]) - expected = [ - mock.call( - table=constants.DVR_TO_SRC_MAC, - dl_dst=self._compute_port.vif_mac, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan))] - if ip_version == 4: - expected.append(mock.call( - table=constants.DVR_TO_SRC_MAC, - proto='ip', nw_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan)) - ) - else: - expected.append(mock.call( - table=constants.DVR_TO_SRC_MAC, - proto='ipv6', ipv6_dst=cidr, - dl_vlan=( - self.agent.local_vlan_map[self._net_uuid].vlan)) - ) - self.assertEqual(expected, delete_flows_int_fn.call_args_list) - - def test_treat_devices_removed_for_dvr_with_compute_ports(self): - self._test_treat_devices_removed_for_dvr( - device_owner="compute:None") - self._test_treat_devices_removed_for_dvr( - device_owner="compute:None", ip_version=6) - - def test_treat_devices_removed_for_dvr_with_lbaas_vip_ports(self): - self._test_treat_devices_removed_for_dvr( - device_owner=n_const.DEVICE_OWNER_LOADBALANCER) - self._test_treat_devices_removed_for_dvr( - device_owner=n_const.DEVICE_OWNER_LOADBALANCER, ip_version=6) - - def test_treat_devices_removed_for_dvr_with_dhcp_ports(self): - self._test_treat_devices_removed_for_dvr( - device_owner=n_const.DEVICE_OWNER_DHCP) - self._test_treat_devices_removed_for_dvr( - device_owner=n_const.DEVICE_OWNER_DHCP, ip_version=6) - - def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10): - self._setup_for_dvr_test() - with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'set_db_attribute', - return_value=True): - with contextlib.nested( - mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.' - 'db_get_val', - return_value=self._old_local_vlan), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', - return_value={'gateway_ip': '1.1.1.1', - 'cidr': '1.1.1.0/24', - 'ip_version': 4, - 'gateway_mac': 'aa:bb:cc:11:22:33'}), - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_ports_on_host_by_subnet', - return_value=[]), - mock.patch.object(self.agent.dvr_agent.int_br, - 'get_vif_port_by_id', - return_value=self._port), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (get_ovs_db_func, get_subnet_fn, get_cphost_fn, - get_vif_fn, add_flow_int_fn, delete_flows_int_fn, - add_flow_tun_fn, delete_flows_tun_fn): - self.agent.port_bound( - self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, - n_const.DEVICE_OWNER_ROUTER_SNAT, - False) - self.assertTrue(add_flow_int_fn.called) - self.assertTrue(delete_flows_int_fn.called) - - with contextlib.nested( - mock.patch.object(self.agent, 'reclaim_local_vlan'), - mock.patch.object(self.agent.plugin_rpc, 'update_device_down', - return_value=None), - mock.patch.object(self.agent.dvr_agent.int_br, - 'delete_flows')) as (reclaim_vlan_fn, - update_dev_down_fn, - delete_flows_int_fn): - self.agent.treat_devices_removed([self._port.vif_id]) - self.assertTrue(delete_flows_int_fn.called) - - def test_setup_dvr_flows_on_int_br(self): - self._setup_for_dvr_test() - with contextlib.nested( - mock.patch.object(self.agent.dvr_agent.int_br, - 'remove_all_flows'), - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object( - self.agent.dvr_agent.plugin_rpc, - 'get_dvr_mac_address_list', - return_value=[{'host': 'cn1', - 'mac_address': 'aa:bb:cc:dd:ee:ff'}, - {'host': 'cn2', - 'mac_address': '11:22:33:44:55:66'}])) as \ - (remove_flows_fn, add_int_flow_fn, add_tun_flow_fn, - get_mac_list_fn): - self.agent.dvr_agent.setup_dvr_flows_on_integ_tun_br() - self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) - self.assertTrue(remove_flows_fn.called) - self.assertEqual(add_int_flow_fn.call_count, 5) - self.assertEqual(add_tun_flow_fn.call_count, 5) - - def test_get_dvr_mac_address(self): - self._setup_for_dvr_test() - self.agent.dvr_agent.dvr_mac_address = None - with mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_dvr_mac_address_by_host', - return_value={'host': 'cn1', - 'mac_address': 'aa:22:33:44:55:66'}): - - self.agent.dvr_agent.get_dvr_mac_address() - self.assertEqual('aa:22:33:44:55:66', - self.agent.dvr_agent.dvr_mac_address) - self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) - - def test_get_dvr_mac_address_exception(self): - self._setup_for_dvr_test() - self.agent.dvr_agent.dvr_mac_address = None - with contextlib.nested( - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_dvr_mac_address_by_host', - side_effect=messaging.RemoteError), - mock.patch.object(self.agent.dvr_agent.int_br, - 'add_flow')) as (gd_mac, add_int_flow_fn): - - self.agent.dvr_agent.get_dvr_mac_address() - self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) - self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) - self.assertEqual(add_int_flow_fn.call_count, 1) - - def test_get_dvr_mac_address_retried(self): - valid_entry = {'host': 'cn1', 'mac_address': 'aa:22:33:44:55:66'} - raise_timeout = messaging.MessagingTimeout() - # Raise a timeout the first 2 times it calls get_dvr_mac_address() - self._setup_for_dvr_test() - self.agent.dvr_agent.dvr_mac_address = None - with mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_dvr_mac_address_by_host', - side_effect=(raise_timeout, raise_timeout, - valid_entry)): - - self.agent.dvr_agent.get_dvr_mac_address() - self.assertEqual('aa:22:33:44:55:66', - self.agent.dvr_agent.dvr_mac_address) - self.assertTrue(self.agent.dvr_agent.in_distributed_mode()) - self.assertEqual(self.agent.dvr_agent.plugin_rpc. - get_dvr_mac_address_by_host.call_count, 3) - - def test_get_dvr_mac_address_retried_max(self): - raise_timeout = messaging.MessagingTimeout() - # Raise a timeout every time until we give up, currently 5 tries - self._setup_for_dvr_test() - self.agent.dvr_agent.dvr_mac_address = None - with contextlib.nested( - mock.patch.object(self.agent.dvr_agent.plugin_rpc, - 'get_dvr_mac_address_by_host', - side_effect=raise_timeout), - mock.patch.object(utils, "execute"), - ) as (rpc_mock, execute_mock): - self.agent.dvr_agent.get_dvr_mac_address() - self.assertIsNone(self.agent.dvr_agent.dvr_mac_address) - self.assertFalse(self.agent.dvr_agent.in_distributed_mode()) - self.assertEqual(self.agent.dvr_agent.plugin_rpc. - get_dvr_mac_address_by_host.call_count, 5) - def _test_port_dead(self, cur_tag=None): port = mock.Mock() port.ofport = 1 @@ -1447,35 +883,6 @@ class TestOvsNeutronAgent(base.BaseTestCase): self.agent.reclaim_local_vlan('net2') del_port_fn.assert_called_once_with('gre-02020202') - def test_dvr_mac_address_update(self): - self._setup_for_dvr_test() - with contextlib.nested( - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows'), - #mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows') - ) as (add_flow_fn, add_flow_tn_fn, del_flows_fn): - self.agent.dvr_agent.\ - dvr_mac_address_update( - dvr_macs=[{'host': 'cn2', - 'mac_address': 'aa:bb:cc:dd:ee:ff'}]) - add_flow_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN, - priority=1, - dl_src='aa:bb:cc:dd:ee:ff', - actions="output:%s" - % self.agent.patch_int_ofport - ) - self.assertFalse(del_flows_fn.called) - with contextlib.nested( - mock.patch.object(self.agent.dvr_agent.int_br, 'add_flow'), - mock.patch.object(self.agent.dvr_agent.tun_br, 'delete_flows'), - mock.patch.object(self.agent.dvr_agent.int_br, 'delete_flows') - ) as (add_flow_fn, del_flows_tn_fn, del_flows_fn): - self.agent.dvr_agent.dvr_mac_address_update(dvr_macs=[]) - del_flows_tn_fn.assert_called_with(table=constants.DVR_NOT_LEARN, - dl_src='aa:bb:cc:dd:ee:ff') - self.assertFalse(add_flow_fn.called) - def test_daemon_loop_uses_polling_manager(self): with mock.patch( 'neutron.agent.linux.polling.get_polling_manager') as mock_get_pm: -- 2.45.2