From bd5373b670cdd7f21f8a1ece98fde6be9fda71ab Mon Sep 17 00:00:00 2001 From: yangxurong Date: Tue, 26 Aug 2014 15:15:40 +0800 Subject: [PATCH] Use iptables zone to separate different ip_conntrack ip_conntrack causes security group rule failures when packets share the same 5-tuple. Use iptables zone option to separate different conntrack zone. Currently this patch only works for OVS agent. Co-authored-by: shihanzhang Change-Id: I90b4d2485e3e491f496dfb7bdee03d57f393be35 Partial-Bug: #1359523 --- neutron/agent/linux/iptables_firewall.py | 46 +++- neutron/agent/linux/iptables_manager.py | 9 +- neutron/agent/securitygroups_rpc.py | 23 +- .../openvswitch/agent/ovs_neutron_agent.py | 137 +++++++---- .../agent/linux/test_iptables_firewall.py | 18 +- .../unit/agent/linux/test_iptables_manager.py | 30 ++- .../unit/agent/test_securitygroups_rpc.py | 212 +++++++++++++++--- .../agent/test_ovs_neutron_agent.py | 158 ++++++------- .../plugins/openvswitch/test_ovs_tunnel.py | 22 +- 9 files changed, 459 insertions(+), 196 deletions(-) diff --git a/neutron/agent/linux/iptables_firewall.py b/neutron/agent/linux/iptables_firewall.py index 4368dad856a..323d3501909 100644 --- a/neutron/agent/linux/iptables_firewall.py +++ b/neutron/agent/linux/iptables_firewall.py @@ -193,9 +193,17 @@ class IptablesFirewallDriver(firewall.FirewallDriver): self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP', comment=ic.UNMATCH_DROP) + def _add_raw_chain(self, chain_name): + self.iptables.ipv4['raw'].add_chain(chain_name) + self.iptables.ipv6['raw'].add_chain(chain_name) + def _add_chain_by_name_v4v6(self, chain_name): - self.iptables.ipv6['filter'].add_chain(chain_name) self.iptables.ipv4['filter'].add_chain(chain_name) + self.iptables.ipv6['filter'].add_chain(chain_name) + + def _remove_raw_chain(self, chain_name): + self.iptables.ipv4['raw'].remove_chain(chain_name) + self.iptables.ipv6['raw'].remove_chain(chain_name) def _remove_chain_by_name_v4v6(self, chain_name): self.iptables.ipv4['filter'].remove_chain(chain_name) @@ -676,3 +684,39 @@ class OVSHybridIptablesFirewallDriver(IptablesFirewallDriver): def _get_device_name(self, port): return (self.OVS_HYBRID_TAP_PREFIX + port['device'])[:LINUX_DEV_LEN] + + def _get_br_device_name(self, port): + return ('qvb' + port['device'])[:LINUX_DEV_LEN] + + def _get_jump_rule(self, port, direction): + if direction == INGRESS_DIRECTION: + device = self._get_br_device_name(port) + else: + device = self._get_device_name(port) + jump_rule = '-m physdev --physdev-in %s -j CT --zone %s' % ( + device, port['zone_id']) + return jump_rule + + def _add_raw_chain_rules(self, port, direction): + if port['zone_id']: + jump_rule = self._get_jump_rule(port, direction) + self.iptables.ipv4['raw'].add_rule('PREROUTING', jump_rule) + self.iptables.ipv6['raw'].add_rule('PREROUTING', jump_rule) + + def _remove_raw_chain_rules(self, port, direction): + if port['zone_id']: + jump_rule = self._get_jump_rule(port, direction) + self.iptables.ipv4['raw'].remove_rule('PREROUTING', jump_rule) + self.iptables.ipv6['raw'].remove_rule('PREROUTING', jump_rule) + + def _add_chain(self, port, direction): + super(OVSHybridIptablesFirewallDriver, self)._add_chain(port, + direction) + if direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]: + self._add_raw_chain_rules(port, direction) + + def _remove_chain(self, port, direction): + super(OVSHybridIptablesFirewallDriver, self)._remove_chain(port, + direction) + if direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]: + self._remove_raw_chain_rules(port, direction) diff --git a/neutron/agent/linux/iptables_manager.py b/neutron/agent/linux/iptables_manager.py index 28b89952ed9..ed99155d174 100644 --- a/neutron/agent/linux/iptables_manager.py +++ b/neutron/agent/linux/iptables_manager.py @@ -326,10 +326,11 @@ class IptablesManager(object): {'nat': IptablesTable(binary_name=self.wrap_name)}) builtin_chains[4].update({'nat': ['PREROUTING', 'OUTPUT', 'POSTROUTING']}) - self.ipv4.update( - {'raw': IptablesTable(binary_name=self.wrap_name)}) - builtin_chains[4].update({'raw': ['PREROUTING', - 'OUTPUT']}) + + self.ipv4.update({'raw': IptablesTable(binary_name=self.wrap_name)}) + builtin_chains[4].update({'raw': ['PREROUTING', 'OUTPUT']}) + self.ipv6.update({'raw': IptablesTable(binary_name=self.wrap_name)}) + builtin_chains[6].update({'raw': ['PREROUTING', 'OUTPUT']}) for ip_version in builtin_chains: if ip_version == 4: diff --git a/neutron/agent/securitygroups_rpc.py b/neutron/agent/securitygroups_rpc.py index 5b24dbe3af2..a7d2fb46dac 100644 --- a/neutron/agent/securitygroups_rpc.py +++ b/neutron/agent/securitygroups_rpc.py @@ -84,10 +84,12 @@ def disable_security_group_extension_by_config(aliases): class SecurityGroupAgentRpc(object): """Enables SecurityGroup agent support in agent implementations.""" - def __init__(self, context, plugin_rpc, defer_refresh_firewall=False): + def __init__(self, context, plugin_rpc, local_vlan_map=None, + defer_refresh_firewall=False,): self.context = context self.plugin_rpc = plugin_rpc self.init_firewall(defer_refresh_firewall) + self.local_vlan_map = local_vlan_map def init_firewall(self, defer_refresh_firewall=False): firewall_driver = cfg.CONF.SECURITYGROUP.firewall_driver @@ -108,6 +110,23 @@ class SecurityGroupAgentRpc(object): self.global_refresh_firewall = False self._use_enhanced_rpc = None + def set_local_zone(self, device): + """Set local zone id for device + + In order to separate conntrack in different networks, a local zone + id is needed to generate related iptables rules. This routine sets + zone id to device according to the network it belongs to. For OVS + agent, vlan id of each network can be used as zone id. + + :param device: dictionary of device information, get network id by + device['network_id'], and set zone id by device['zone_id'] + """ + net_id = device['network_id'] + zone_id = None + if self.local_vlan_map and net_id in self.local_vlan_map: + zone_id = self.local_vlan_map[net_id].vlan + device['zone_id'] = zone_id + @property def use_enhanced_rpc(self): if self._use_enhanced_rpc is None: @@ -157,6 +176,7 @@ class SecurityGroupAgentRpc(object): with self.firewall.defer_apply(): for device in devices.values(): + self.set_local_zone(device) self.firewall.prepare_port_filter(device) if self.use_enhanced_rpc: LOG.debug("Update security group information for ports %s", @@ -244,6 +264,7 @@ class SecurityGroupAgentRpc(object): with self.firewall.defer_apply(): for device in devices.values(): LOG.debug("Update port filter for %s", device['device']) + self.set_local_zone(device) self.firewall.update_port_filter(device) if self.use_enhanced_rpc: LOG.debug("Update security group information for ports %s", diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 0be1b9c7e54..0cfd824a6a3 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -254,9 +254,14 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # Collect additional bridges to monitor self.ancillary_brs = self.setup_ancillary_bridges(integ_br, tun_br) + # In order to keep existed device's local vlan unchanged, + # restore local vlan mapping at start + self._restore_local_vlan_map() + # Security group agent support self.sg_agent = sg_rpc.SecurityGroupAgentRpc(self.context, - self.sg_plugin_rpc, defer_refresh_firewall=True) + self.sg_plugin_rpc, self.local_vlan_map, + defer_refresh_firewall=True) # Initialize iteration counter self.iter_num = 0 @@ -283,6 +288,21 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, except Exception: LOG.exception(_LE("Failed reporting state!")) + def _restore_local_vlan_map(self): + cur_ports = self.int_br.get_vif_ports() + for port in cur_ports: + local_vlan_map = self.int_br.db_get_val("Port", port.port_name, + "other_config") + local_vlan = self.int_br.db_get_val("Port", port.port_name, "tag") + net_uuid = local_vlan_map.get('net_uuid') + if (net_uuid and net_uuid not in self.local_vlan_map + and local_vlan != DEAD_VLAN_TAG): + self.provision_local_vlan(local_vlan_map['net_uuid'], + local_vlan_map['network_type'], + local_vlan_map['physical_network'], + local_vlan_map['segmentation_id'], + local_vlan) + def setup_rpc(self): self.agent_id = 'ovs-agent-%s' % cfg.CONF.host self.topic = topics.AGENT @@ -496,7 +516,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, LOG.warning(_LW('Action %s not supported'), action) def provision_local_vlan(self, net_uuid, network_type, physical_network, - segmentation_id): + segmentation_id, local_vlan=None): '''Provisions a local VLAN. :param net_uuid: the uuid of the network associated with this vlan. @@ -513,11 +533,15 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if lvm: lvid = lvm.vlan else: - if not self.available_local_vlans: - LOG.error(_LE("No local VLAN available for net-id=%s"), - net_uuid) - return - lvid = self.available_local_vlans.pop() + if local_vlan in self.available_local_vlans: + lvid = local_vlan + self.available_local_vlans.remove(local_vlan) + else: + if not self.available_local_vlans: + LOG.error(_LE("No local VLAN available for net-id=%s"), + net_uuid) + return + lvid = self.available_local_vlans.pop() self.local_vlan_map[net_uuid] = LocalVLANMapping(lvid, network_type, physical_network, @@ -697,14 +721,43 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.dvr_agent.bind_port_to_dvr(port, lvm, fixed_ips, device_owner) + port_other_config = self.int_br.db_get_val("Port", port.port_name, + "other_config") + vlan_mapping = {'net_uuid': net_uuid, + 'network_type': network_type, + 'physical_network': physical_network, + 'segmentation_id': segmentation_id} + port_other_config.update(vlan_mapping) + self.int_br.set_db_attribute("Port", port.port_name, "other_config", + port_other_config) - # Do not bind a port if it's already bound - cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") - if cur_tag != lvm.vlan: - self.int_br.set_db_attribute("Port", port.port_name, "tag", - lvm.vlan) - if port.ofport != -1: - self.int_br.delete_flows(in_port=port.ofport) + def _bind_devices(self, need_binding_ports): + for port_detail in need_binding_ports: + lvm = self.local_vlan_map[port_detail['network_id']] + port = port_detail['vif_port'] + device = port_detail['device'] + # Do not bind a port if it's already bound + cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") + if cur_tag != lvm.vlan: + self.int_br.set_db_attribute( + "Port", port.port_name, "tag", lvm.vlan) + if port.ofport != -1: + self.int_br.delete_flows(in_port=port.ofport) + + # update plugin about port status + # FIXME(salv-orlando): Failures while updating device status + # must be handled appropriately. Otherwise this might prevent + # neutron server from sending network-vif-* events to the nova + # API server, thus possibly preventing instance spawn. + if port_detail.get('admin_state_up'): + LOG.debug("Setting status for %s to UP", device) + self.plugin_rpc.update_device_up( + self.context, device, self.agent_id, cfg.CONF.host) + else: + LOG.debug("Setting status for %s to DOWN", device) + self.plugin_rpc.update_device_down( + self.context, device, self.agent_id, cfg.CONF.host) + LOG.info(_LI("Configuration for device %s completed."), device) @staticmethod def setup_arp_spoofing_protection(bridge, vif, port_details): @@ -1150,6 +1203,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # an OVS ofport configured, as only these ports were considered # for being treated. If that does not happen, it is a potential # error condition of which operators should be aware + port_needs_binding = True if not vif_port.ofport: LOG.warn(_LW("VIF port: %s has no ofport configured, " "and might not be able to transmit"), vif_port.vif_id) @@ -1160,8 +1214,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, fixed_ips, device_owner, ovs_restarted) else: self.port_dead(vif_port) + port_needs_binding = False else: LOG.debug("No VIF port for port %s defined on agent.", port_id) + return port_needs_binding def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type): ofport = br.add_tunnel_port(port_name, @@ -1222,6 +1278,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, def treat_devices_added_or_updated(self, devices, ovs_restarted): skipped_devices = [] + need_binding_devices = [] try: devices_details_list = self.plugin_rpc.get_devices_details_list( self.context, @@ -1244,37 +1301,26 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if 'port_id' in details: LOG.info(_LI("Port %(device)s updated. Details: %(details)s"), {'device': device, 'details': details}) - self.treat_vif_port(port, details['port_id'], - details['network_id'], - details['network_type'], - details['physical_network'], - details['segmentation_id'], - details['admin_state_up'], - details['fixed_ips'], - details['device_owner'], - ovs_restarted) + need_binding = self.treat_vif_port(port, details['port_id'], + details['network_id'], + details['network_type'], + details['physical_network'], + details['segmentation_id'], + details['admin_state_up'], + details['fixed_ips'], + details['device_owner'], + ovs_restarted) if self.prevent_arp_spoofing: self.setup_arp_spoofing_protection(self.int_br, port, details) - # update plugin about port status - # FIXME(salv-orlando): Failures while updating device status - # must be handled appropriately. Otherwise this might prevent - # neutron server from sending network-vif-* events to the nova - # API server, thus possibly preventing instance spawn. - if details.get('admin_state_up'): - LOG.debug("Setting status for %s to UP", device) - self.plugin_rpc.update_device_up( - self.context, device, self.agent_id, cfg.CONF.host) - else: - LOG.debug("Setting status for %s to DOWN", device) - self.plugin_rpc.update_device_down( - self.context, device, self.agent_id, cfg.CONF.host) - LOG.info(_LI("Configuration for device %s completed."), device) + if need_binding: + details['vif_port'] = port + need_binding_devices.append(details) else: LOG.warn(_LW("Device %s not defined on plugin"), device) if (port and port.ofport != -1): self.port_dead(port) - return skipped_devices + return skipped_devices, need_binding_devices def treat_ancillary_devices_added(self, devices): try: @@ -1344,10 +1390,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # sources: the neutron server, and the ovs db monitor process # If there is an exception while processing security groups ports # will not be wired anyway, and a resync will be triggered - # TODO(salv-orlando): Optimize avoiding applying filters unnecessarily - # (eg: when there are no IP address changes) - self.sg_agent.setup_port_filters(port_info.get('added', set()), - port_info.get('updated', set())) # VIF wiring needs to be performed always for 'new' devices. # For updated ports, re-wiring is not needed in most cases, but needs # to be performed anyway when the admin state of a device is changed. @@ -1358,8 +1400,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if devices_added_updated: start = time.time() try: - skipped_devices = self.treat_devices_added_or_updated( - devices_added_updated, ovs_restarted) + skipped_devices, need_binding_devices = ( + self.treat_devices_added_or_updated( + devices_added_updated, ovs_restarted)) LOG.debug("process_network_ports - iteration:%(iter_num)d - " "treat_devices_added_or_updated completed. " "Skipped %(num_skipped)d devices of " @@ -1369,6 +1412,12 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, 'num_skipped': len(skipped_devices), 'num_current': len(port_info['current']), 'elapsed': time.time() - start}) + # TODO(salv-orlando): Optimize avoiding applying filters + # unnecessarily, (eg: when there are no IP address changes) + self.sg_agent.setup_port_filters( + port_info.get('added', set()), + port_info.get('updated', set())) + self._bind_devices(need_binding_devices) # Update the list of current ports storing only those which # have been actually processed. port_info['current'] = (port_info['current'] - diff --git a/neutron/tests/unit/agent/linux/test_iptables_firewall.py b/neutron/tests/unit/agent/linux/test_iptables_firewall.py index cca76a4e5b2..4fa622ccdbb 100644 --- a/neutron/tests/unit/agent/linux/test_iptables_firewall.py +++ b/neutron/tests/unit/agent/linux/test_iptables_firewall.py @@ -56,8 +56,12 @@ class BaseIptablesFirewallTestCase(base.BaseTestCase): self.iptables_inst = mock.Mock() self.v4filter_inst = mock.Mock() self.v6filter_inst = mock.Mock() - self.iptables_inst.ipv4 = {'filter': self.v4filter_inst} - self.iptables_inst.ipv6 = {'filter': self.v6filter_inst} + self.iptables_inst.ipv4 = {'filter': self.v4filter_inst, + 'raw': self.v4filter_inst + } + self.iptables_inst.ipv6 = {'filter': self.v6filter_inst, + 'raw': self.v6filter_inst + } iptables_cls.return_value = self.iptables_inst self.firewall = iptables_firewall.IptablesFirewallDriver() @@ -69,6 +73,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def _fake_port(self): return {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', + 'network_id': 'fake_net', 'fixed_ips': [FAKE_IP['IPv4'], FAKE_IP['IPv6']]} @@ -921,7 +926,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): '-m physdev --physdev-out tapfake_dev ' '--physdev-is-bridged ' '-j $ifake_dev', - comment=ic.SG_TO_VM_SG), + comment=ic.SG_TO_VM_SG) ] if ethertype == 'IPv6': for icmp6_type in constants.ICMPV6_ALLOWED_TYPES: @@ -1247,8 +1252,8 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test_defer_chain_apply_coalesce_multiple_ports(self): chain_applies = self._mock_chain_applies() - port1 = {'device': 'd1', 'mac_address': 'mac1'} - port2 = {'device': 'd2', 'mac_address': 'mac2'} + port1 = {'device': 'd1', 'mac_address': 'mac1', 'network_id': 'net1'} + port2 = {'device': 'd2', 'mac_address': 'mac2', 'network_id': 'net1'} device2port = {'d1': port1, 'd2': port2} with self.firewall.defer_apply(): self.firewall.prepare_port_filter(port1) @@ -1259,6 +1264,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test_ip_spoofing_filter_with_multiple_ips(self): port = {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', + 'network_id': 'fake_net', 'fixed_ips': ['10.0.0.1', 'fe80::1', '10.0.0.2']} self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), @@ -1338,6 +1344,7 @@ class IptablesFirewallTestCase(BaseIptablesFirewallTestCase): def test_ip_spoofing_no_fixed_ips(self): port = {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', + 'network_id': 'fake_net', 'fixed_ips': []} self.firewall.prepare_port_filter(port) calls = [mock.call.add_chain('sg-fallback'), @@ -1421,6 +1428,7 @@ class IptablesFirewallEnhancedIpsetTestCase(BaseIptablesFirewallTestCase): def _fake_port(self, sg_id=FAKE_SGID): return {'device': 'tapfake_dev', 'mac_address': 'ff:ff:ff:ff:ff:ff', + 'network_id': 'fake_net', 'fixed_ips': [FAKE_IP['IPv4'], FAKE_IP['IPv6']], 'security_groups': [sg_id], diff --git a/neutron/tests/unit/agent/linux/test_iptables_manager.py b/neutron/tests/unit/agent/linux/test_iptables_manager.py index 4a1457c07b7..5b8dcb1df2a 100644 --- a/neutron/tests/unit/agent/linux/test_iptables_manager.py +++ b/neutron/tests/unit/agent/linux/test_iptables_manager.py @@ -314,7 +314,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - filter_dump_ipv6) + raw_dump + filter_dump_ipv6) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -374,7 +374,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - filter_dump) + raw_dump + filter_dump) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -421,7 +421,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + RAW_DUMP + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -452,6 +452,8 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): iptables_args['filter_rules'] = filter_rules filter_dump_mod = FILTER_WITH_RULES_TEMPLATE % iptables_args + raw_dump = RAW_DUMP % IPTABLES_ARG + expected_calls_and_values = [ (mock.call(['iptables-save', '-c'], run_as_root=True), @@ -473,7 +475,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + raw_dump + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -533,6 +535,8 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): '# Completed by iptables_manager\n' % iptables_args) + raw_dump = RAW_DUMP % IPTABLES_ARG + expected_calls_and_values = [ (mock.call(['iptables-save', '-c'], run_as_root=True), @@ -553,7 +557,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + raw_dump + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -624,7 +628,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + RAW_DUMP + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -680,6 +684,8 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): '# Completed by iptables_manager\n' % IPTABLES_ARG) + raw_dump = RAW_DUMP % IPTABLES_ARG + expected_calls_and_values = [ (mock.call(['iptables-save', '-c'], run_as_root=True), @@ -700,7 +706,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + raw_dump + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -769,7 +775,7 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ] if use_ipv6: self._extend_with_ip6tables_filter(expected_calls_and_values, - FILTER_DUMP) + RAW_DUMP + FILTER_DUMP) tools.setup_mock_calls(self.execute, expected_calls_and_values) @@ -911,6 +917,10 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): ''), ] if use_ipv6: + expected_calls_and_values.append( + (mock.call(['ip6tables', '-t', 'raw', '-L', 'OUTPUT', + '-n', '-v', '-x'], run_as_root=True), + '')) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x'], @@ -960,6 +970,10 @@ class IptablesManagerStateFulTestCase(base.BaseTestCase): '') ] if use_ipv6: + expected_calls_and_values.append( + (mock.call(['ip6tables', '-t', 'raw', '-L', 'OUTPUT', + '-n', '-v', '-x', '-Z'], run_as_root=True), + '')) expected_calls_and_values.append( (mock.call(['ip6tables', '-t', 'filter', '-L', 'OUTPUT', '-n', '-v', '-x', '-Z'], diff --git a/neutron/tests/unit/agent/test_securitygroups_rpc.py b/neutron/tests/unit/agent/test_securitygroups_rpc.py index 8dd6c90b0a6..0cb587f7de5 100644 --- a/neutron/tests/unit/agent/test_securitygroups_rpc.py +++ b/neutron/tests/unit/agent/test_securitygroups_rpc.py @@ -13,9 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import contextlib -import collections import mock from oslo_config import cfg import oslo_messaging @@ -34,6 +34,7 @@ from neutron.db import securitygroups_rpc_base as sg_db_rpc from neutron.extensions import allowedaddresspairs as addr_pair from neutron.extensions import securitygroup as ext_sg from neutron import manager +from neutron.plugins.openvswitch.agent import ovs_neutron_agent from neutron.tests import base from neutron.tests.unit.extensions import test_securitygroup as test_sg @@ -1107,6 +1108,7 @@ class BaseSecurityGroupAgentRpcTestCase(base.BaseTestCase): self.firewall.defer_apply.side_effect = firewall_object.defer_apply self.agent.firewall = self.firewall self.fake_device = {'device': 'fake_device', + 'network_id': 'fake_net', 'security_groups': ['fake_sgid1', 'fake_sgid2'], 'security_group_source_groups': ['fake_sgid2'], 'security_group_rules': [{'security_group_id': @@ -1667,6 +1669,42 @@ IPTABLES_ARG['ip1'] = IPS.values()[0] IPTABLES_ARG['ip2'] = IPS.values()[1] IPTABLES_ARG['chains'] = CHAINS_NAT +IPTABLES_RAW_DEFAULT = """# Generated by iptables_manager +*raw +:%(bn)s-OUTPUT - [0:0] +:%(bn)s-PREROUTING - [0:0] +[0:0] -A PREROUTING -j %(bn)s-PREROUTING +[0:0] -A OUTPUT -j %(bn)s-OUTPUT +COMMIT +# Completed by iptables_manager +""" % IPTABLES_ARG + +IPTABLES_RAW_DEVICE_1 = """# Generated by iptables_manager +*raw +:%(bn)s-OUTPUT - [0:0] +:%(bn)s-PREROUTING - [0:0] +[0:0] -A PREROUTING -j %(bn)s-PREROUTING +[0:0] -A OUTPUT -j %(bn)s-OUTPUT +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in qvbtap_port1 -j CT --zone 1 +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in tap_port1 -j CT --zone 1 +COMMIT +# Completed by iptables_manager +""" % IPTABLES_ARG + +IPTABLES_RAW_DEVICE_2 = """# Generated by iptables_manager +*raw +:%(bn)s-OUTPUT - [0:0] +:%(bn)s-PREROUTING - [0:0] +[0:0] -A PREROUTING -j %(bn)s-PREROUTING +[0:0] -A OUTPUT -j %(bn)s-OUTPUT +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in qvbtap_port1 -j CT --zone 1 +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in tap_port1 -j CT --zone 1 +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in qvbtap_port2 -j CT --zone 1 +[0:0] -A %(bn)s-PREROUTING -m physdev --physdev-in tap_port2 -j CT --zone 1 +COMMIT +# Completed by iptables_manager +""" % IPTABLES_ARG + IPTABLES_NAT = """# Generated by iptables_manager *nat :neutron-postrouting-bottom - [0:0] @@ -2469,9 +2507,7 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): cfg.CONF.set_override('comment_iptables_rules', False, group='AGENT') self.rpc = mock.Mock() - self.agent = sg_rpc.SecurityGroupAgentRpc( - context=None, plugin_rpc=self.rpc, - defer_refresh_firewall=defer_refresh_firewall) + self._init_agent(defer_refresh_firewall) if test_rpc_v1_1: self.rpc.security_group_info_for_devices.side_effect = ( @@ -2542,8 +2578,14 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): '12:34:56:78:9a:bd', rule5)} + def _init_agent(self, defer_refresh_firewall): + self.agent = sg_rpc.SecurityGroupAgentRpc( + context=None, plugin_rpc=self.rpc, + defer_refresh_firewall=defer_refresh_firewall) + def _device(self, device, ip, mac_address, rule): return {'device': device, + 'network_id': 'fakenet', 'fixed_ips': [ip], 'mac_address': mac_address, 'security_groups': ['security_group1'], @@ -2588,14 +2630,14 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): self.assertThat(kwargs['process_input'], matchers.MatchesRegex(expected_regex)) - def _replay_iptables(self, v4_filter, v6_filter): + def _replay_iptables(self, v4_filter, v6_filter, raw): self._register_mock_call( ['iptables-save', '-c'], run_as_root=True, return_value='') self._register_mock_call( ['iptables-restore', '-c'], - process_input=self._regex(IPTABLES_RAW + IPTABLES_NAT + + process_input=self._regex(raw + IPTABLES_NAT + IPTABLES_MANGLE + v4_filter), run_as_root=True, return_value='') @@ -2605,14 +2647,16 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): return_value='') self._register_mock_call( ['ip6tables-restore', '-c'], - process_input=self._regex(v6_filter), + process_input=self._regex(raw + v6_filter), run_as_root=True, return_value='') def test_prepare_remove_port(self): self.rpc.security_group_rules_for_devices.return_value = self.devices1 - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) @@ -2621,12 +2665,18 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): def test_security_group_member_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices1 - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.rpc.security_group_rules_for_devices.return_value = self.devices2 @@ -2641,8 +2691,10 @@ class TestSecurityGroupAgentWithIptables(base.BaseTestCase): def test_security_group_rule_updated(self): self.rpc.security_group_rules_for_devices.return_value = self.devices2 - self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2) + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.rpc.security_group_rules_for_devices.return_value = self.devices3 @@ -2713,8 +2765,10 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( def test_prepare_remove_port(self): self.sg_info.return_value = self.devices_info1 - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) @@ -2723,12 +2777,18 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( def test_security_group_member_updated(self): self.sg_info.return_value = self.devices_info1 - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.sg_info.return_value = self.devices_info2 @@ -2743,8 +2803,10 @@ class TestSecurityGroupAgentEnhancedRpcWithIptables( def test_security_group_rule_updated(self): self.sg_info.return_value = self.devices_info2 - self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2) + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3 @@ -2765,8 +2827,10 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( def test_prepare_remove_port(self): self.sg_info.return_value = self.devices_info1 - self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.agent.remove_devices_filter(['tap_port1']) @@ -2775,12 +2839,18 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( def test_security_group_member_updated(self): self.sg_info.return_value = self.devices_info1 - self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1) - self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY) + self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPSET_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1']) self.sg_info.return_value = self.devices_info2 @@ -2795,8 +2865,10 @@ class TestSecurityGroupAgentEnhancedIpsetWithIptables( def test_security_group_rule_updated(self): self.sg_info.return_value = self.devices_info2 - self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2) - self._replay_iptables(IPSET_FILTER_2_3, IPTABLES_FILTER_V6_2) + self._replay_iptables(IPSET_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) + self._replay_iptables(IPSET_FILTER_2_3, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEFAULT) self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) self.sg_info.return_value = self.devices_info3 @@ -2858,11 +2930,77 @@ class TestSecurityGroupAgentWithOVSIptables( FIREWALL_DRIVER = FIREWALL_HYBRID_DRIVER + def setUp(self, defer_refresh_firewall=False, test_rpc_v1_1=True): + super(TestSecurityGroupAgentWithOVSIptables, self).setUp( + defer_refresh_firewall, + test_rpc_v1_1) + + def _init_agent(self, defer_refresh_firewall): + fake_map = ovs_neutron_agent.LocalVLANMapping(1, 'network_type', + 'physical_network', 1) + local_vlan_map = {'fakenet': fake_map} + self.agent = sg_rpc.SecurityGroupAgentRpc( + context=None, plugin_rpc=self.rpc, + local_vlan_map=local_vlan_map, + defer_refresh_firewall=defer_refresh_firewall) + + def test_prepare_remove_port(self): + self.rpc.security_group_rules_for_devices.return_value = self.devices1 + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEVICE_1) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) + + self.agent.prepare_devices_filter(['tap_port1']) + self.agent.remove_devices_filter(['tap_port1']) + + self._verify_mock_calls() + + def test_security_group_member_updated(self): + self.rpc.security_group_rules_for_devices.return_value = self.devices1 + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEVICE_1) + self._replay_iptables(IPTABLES_FILTER_1_2, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEVICE_1) + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEVICE_2) + self._replay_iptables(IPTABLES_FILTER_2_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEVICE_2) + self._replay_iptables(IPTABLES_FILTER_1, IPTABLES_FILTER_V6_1, + IPTABLES_RAW_DEVICE_1) + self._replay_iptables(IPTABLES_FILTER_EMPTY, IPTABLES_FILTER_V6_EMPTY, + IPTABLES_RAW_DEFAULT) + + self.agent.prepare_devices_filter(['tap_port1']) + self.rpc.security_group_rules_for_devices.return_value = self.devices2 + self.agent.security_groups_member_updated(['security_group1']) + self.agent.prepare_devices_filter(['tap_port2']) + self.rpc.security_group_rules_for_devices.return_value = self.devices1 + self.agent.security_groups_member_updated(['security_group1']) + self.agent.remove_devices_filter(['tap_port2']) + self.agent.remove_devices_filter(['tap_port1']) + + self._verify_mock_calls() + + def test_security_group_rule_updated(self): + self.rpc.security_group_rules_for_devices.return_value = self.devices2 + self._replay_iptables(IPTABLES_FILTER_2, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEVICE_2) + self._replay_iptables(IPTABLES_FILTER_2_3, IPTABLES_FILTER_V6_2, + IPTABLES_RAW_DEVICE_2) + + self.agent.prepare_devices_filter(['tap_port1', 'tap_port3']) + self.rpc.security_group_rules_for_devices.return_value = self.devices3 + self.agent.security_groups_rule_updated(['security_group1']) + + self._verify_mock_calls() + def _regex(self, value): #Note(nati): tap is prefixed on the device # in the OVSHybridIptablesFirewallDriver value = value.replace('tap_port', 'taptap_port') + value = value.replace('qvbtaptap_port', 'qvbtap_port') value = value.replace('o_port', 'otap_port') value = value.replace('i_port', 'itap_port') value = value.replace('s_port', 'stap_port') diff --git a/neutron/tests/unit/plugins/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/openvswitch/agent/test_ovs_neutron_agent.py index 1b8b73f16cf..d201574527e 100644 --- a/neutron/tests/unit/plugins/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/openvswitch/agent/test_ovs_neutron_agent.py @@ -134,7 +134,9 @@ class TestOvsNeutronAgent(base.BaseTestCase): mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), mock.patch('neutron.openstack.common.loopingcall.' 'FixedIntervalLoopingCall', - new=MockFixedIntervalLoopingCall)): + new=MockFixedIntervalLoopingCall), + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + 'get_vif_ports', return_value=[])): self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs) # set back to true because initial report state will succeed due # to mocked out RPC calls @@ -157,22 +159,17 @@ class TestOvsNeutronAgent(base.BaseTestCase): mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' - 'db_get_val', return_value=old_local_vlan), + 'db_get_val', return_value={}), mock.patch.object(self.agent.int_br, 'delete_flows') ) as (set_ovs_db_func, get_ovs_db_func, delete_flows_func): self.agent.port_bound(port, net_uuid, 'local', None, None, fixed_ips, "compute:None", False) - get_ovs_db_func.assert_called_once_with("Port", mock.ANY, "tag") - if new_local_vlan != old_local_vlan: - set_ovs_db_func.assert_called_once_with( - "Port", mock.ANY, "tag", new_local_vlan) - if ofport != -1: - delete_flows_func.assert_called_once_with(in_port=port.ofport) - else: - self.assertFalse(delete_flows_func.called) - else: - self.assertFalse(set_ovs_db_func.called) - self.assertFalse(delete_flows_func.called) + vlan_mapping = {'net_uuid': net_uuid, + 'network_type': 'local', + 'physical_network': None, + 'segmentation_id': None} + set_ovs_db_func.assert_called_once_with( + "Port", mock.ANY, "other_config", vlan_mapping) def test_check_agent_configurations_for_dvr_raises(self): self.agent.enable_distributed_routing = True @@ -339,7 +336,8 @@ class TestOvsNeutronAgent(base.BaseTestCase): mock.patch.object(self.agent.plugin_rpc, 'update_device_down'), mock.patch.object(self.agent, func_name) ) as (get_dev_fn, get_vif_func, upd_dev_up, upd_dev_down, func): - skip_devs = self.agent.treat_devices_added_or_updated([{}], False) + skip_devs, need_bound_devices = ( + self.agent.treat_devices_added_or_updated([{}], False)) # The function should not raise self.assertFalse(skip_devs) return func.called @@ -379,18 +377,13 @@ class TestOvsNeutronAgent(base.BaseTestCase): return_value=[dev_mock]), mock.patch.object(self.agent.int_br, 'get_vif_port_by_id', return_value=None), - mock.patch.object(self.agent.plugin_rpc, 'update_device_up'), - mock.patch.object(self.agent.plugin_rpc, 'update_device_down'), mock.patch.object(self.agent, 'treat_vif_port') - ) as (get_dev_fn, get_vif_func, upd_dev_up, - upd_dev_down, treat_vif_port): + ) as (get_dev_fn, get_vif_func, treat_vif_port): skip_devs = self.agent.treat_devices_added_or_updated([{}], False) # The function should return False for resync and no device # processed - self.assertEqual(['the_skipped_one'], skip_devs) + self.assertEqual((['the_skipped_one'], []), skip_devs) self.assertFalse(treat_vif_port.called) - self.assertFalse(upd_dev_down.called) - self.assertFalse(upd_dev_up.called) def test_treat_devices_added_updated_put_port_down(self): fake_details_dict = {'admin_state_up': False, @@ -411,16 +404,13 @@ class TestOvsNeutronAgent(base.BaseTestCase): return_value=[fake_details_dict]), mock.patch.object(self.agent.int_br, 'get_vif_port_by_id', return_value=mock.MagicMock()), - mock.patch.object(self.agent.plugin_rpc, 'update_device_up'), - mock.patch.object(self.agent.plugin_rpc, 'update_device_down'), mock.patch.object(self.agent, 'treat_vif_port') - ) as (get_dev_fn, get_vif_func, upd_dev_up, - upd_dev_down, treat_vif_port): - skip_devs = self.agent.treat_devices_added_or_updated([{}], False) + ) as (get_dev_fn, get_vif_func, treat_vif_port): + skip_devs, need_bound_devices = ( + self.agent.treat_devices_added_or_updated([{}], False)) # The function should return False for resync self.assertFalse(skip_devs) self.assertTrue(treat_vif_port.called) - self.assertTrue(upd_dev_down.called) def test_treat_devices_removed_returns_true_for_missing_device(self): with mock.patch.object(self.agent.plugin_rpc, 'update_device_down', @@ -445,7 +435,7 @@ class TestOvsNeutronAgent(base.BaseTestCase): with contextlib.nested( mock.patch.object(self.agent.sg_agent, "setup_port_filters"), mock.patch.object(self.agent, "treat_devices_added_or_updated", - return_value=[]), + return_value=([], [])), mock.patch.object(self.agent, "treat_devices_removed", return_value=False) ) as (setup_port_filters, device_added_updated, device_removed): @@ -1267,7 +1257,9 @@ class AncillaryBridgesTest(base.BaseTestCase): return_value=bridges), mock.patch('neutron.agent.common.ovs_lib.BaseOVS.' 'get_bridge_external_bridge_id', - side_effect=pullup_side_effect)): + side_effect=pullup_side_effect), + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + 'get_vif_ports', return_value=[])): self.agent = ovs_neutron_agent.OVSNeutronAgent(**self.kwargs) self.assertEqual(len(ancillary), len(self.agent.ancillary_brs)) if ancillary: @@ -1326,7 +1318,9 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges'), mock.patch('neutron.openstack.common.loopingcall.' 'FixedIntervalLoopingCall', - new=MockFixedIntervalLoopingCall)): + new=MockFixedIntervalLoopingCall), + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + 'get_vif_ports', return_value=[])): self.agent = ovs_neutron_agent.OVSNeutronAgent(**kwargs) # set back to true because initial report state will succeed due # to mocked out RPC calls @@ -1383,13 +1377,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): physical_network = self._physical_network segmentation_id = self._segmentation_id network_type = p_const.TYPE_VLAN - with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={ @@ -1398,13 +1393,12 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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=[]), + '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( @@ -1413,8 +1407,8 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn, add_flow_phys_fn, delete_flows_phys_fn): self.agent.port_bound( @@ -1479,12 +1473,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): ] 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) @@ -1503,13 +1491,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object(self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={ @@ -1524,7 +1513,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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( @@ -1533,8 +1521,8 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn, add_flow_phys_fn, delete_flows_phys_fn): self.agent.port_bound( @@ -1593,12 +1581,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): self.assertEqual(expected_on_int_br, add_flow_int_fn.call_args_list) self.assertFalse(add_flow_phys_fn.called) - 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_phys_fn.called) self.assertFalse(delete_flows_tun_fn.called) self.assertFalse(delete_flows_phys_fn.called) @@ -1635,13 +1617,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10): self._setup_for_dvr_test() - with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object( self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': '1.1.1.1', @@ -1655,11 +1638,10 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', @@ -1667,7 +1649,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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) @@ -1683,13 +1664,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' - with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object( self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, @@ -1703,11 +1685,10 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', @@ -1715,7 +1696,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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'), @@ -1764,13 +1744,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): else: gateway_ip = '2001:100::1' cidr = '2001:100::0/64' - with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object( self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': gateway_ip, @@ -1784,11 +1765,10 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', @@ -1802,7 +1782,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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'), @@ -1841,13 +1820,14 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10): self._setup_for_dvr_test() - with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' + with contextlib.nested( + mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'set_db_attribute', - return_value=True): - with contextlib.nested( + return_value=True), mock.patch('neutron.agent.common.ovs_lib.OVSBridge.' 'db_get_val', - return_value=self._old_local_vlan), + return_value={})): + with contextlib.nested( mock.patch.object( self.agent.dvr_agent.plugin_rpc, 'get_subnet_for_dvr', return_value={'gateway_ip': '1.1.1.1', @@ -1861,11 +1841,10 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): '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, + ) as (get_subnet_fn, get_cphost_fn, + get_vif_fn, add_flow_int_fn, add_flow_tun_fn, delete_flows_tun_fn): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', @@ -1873,7 +1852,6 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase): 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'), diff --git a/neutron/tests/unit/plugins/openvswitch/test_ovs_tunnel.py b/neutron/tests/unit/plugins/openvswitch/test_ovs_tunnel.py index cd21d0477ca..7773d5a0ea7 100644 --- a/neutron/tests/unit/plugins/openvswitch/test_ovs_tunnel.py +++ b/neutron/tests/unit/plugins/openvswitch/test_ovs_tunnel.py @@ -106,6 +106,8 @@ class TunnelTest(base.BaseTestCase): self.mock_int_bridge.add_port.return_value = self.MAP_TUN_INT_OFPORT self.mock_int_bridge.add_patch_port.side_effect = ( lambda tap, peer: self.ovs_int_ofports[tap]) + self.mock_int_bridge.get_vif_ports.return_value = [] + self.mock_int_bridge.db_get_val.return_value = {} self.mock_map_tun_bridge = self.ovs_bridges[self.MAP_TUN_BRIDGE] self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE @@ -190,7 +192,10 @@ class TunnelTest(base.BaseTestCase): mock.call.add_patch_port('patch-int', 'patch-tun'), ] self.mock_int_bridge_expected += [ - mock.call.add_patch_port('patch-tun', 'patch-int') + mock.call.add_patch_port('patch-tun', 'patch-int'), + ] + self.mock_int_bridge_expected += [ + mock.call.get_vif_ports(), ] self.mock_tun_bridge_expected += [ @@ -437,12 +442,15 @@ class TunnelTest(base.BaseTestCase): self._verify_mock_calls() def test_port_bound(self): + vlan_mapping = {'segmentation_id': LS_ID, + 'physical_network': None, + 'net_uuid': NET_UUID, + 'network_type': 'gre'} self.mock_int_bridge_expected += [ - mock.call.db_get_val('Port', VIF_PORT.port_name, 'tag'), + mock.call.db_get_val('Port', 'port', 'other_config'), mock.call.set_db_attribute('Port', VIF_PORT.port_name, - 'tag', LVM.vlan), - mock.call.delete_flows(in_port=VIF_PORT.ofport) - ] + 'other_config', + vlan_mapping)] a = self._build_agent() a.local_vlan_map[NET_UUID] = LVM @@ -610,7 +618,9 @@ class TunnelTestUseVethInterco(TunnelTest): self.mock_int_bridge_expected += [ mock.call.add_patch_port('patch-tun', 'patch-int') ] - + self.mock_int_bridge_expected += [ + mock.call.get_vif_ports(), + ] self.mock_tun_bridge_expected += [ mock.call.remove_all_flows(), mock.call.add_flow(priority=1,