From: Xu Han Peng Date: Wed, 3 Dec 2014 06:58:34 +0000 (+0800) Subject: Fix DVR flow problems for IPv6 subnet X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=727417e71ed155c7d356b27518896026338f79c3;p=openstack-build%2Fneutron-build.git Fix DVR flow problems for IPv6 subnet This code fixes DVR flow problems by changing proto='ip' to proto='ipv6' and changing nw_dst to ipv6_dst. When DVR is enabled, RADVD is spawned by l3 agent on each compute node. This code also prevent IPv6 Router Advertisement from sending to other compute nodes. Change-Id: Id94acd85ea124eff6cfdfbfc546f5dd4ca81ef43 Closes-Bug: 1398244 Closes-Bug: 1398627 Partial-Bug: 1376325 --- diff --git a/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py index cd53e2a58..f7dcfd763 100644 --- a/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_dvr_neutron_agent.py @@ -388,23 +388,27 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): 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: - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - "output:%s" % - (subnet_info['gateway_mac'], ofports)) - - self.tun_br.add_flow(table=constants.DVR_PROCESS, - priority=3, - dl_vlan=local_vlan, - proto='arp', - nw_dst=subnet_info['gateway_ip'], - actions="drop") + 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, @@ -477,14 +481,12 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): if csnat_ofport != constants.OFPORT_INVALID: ofports = str(csnat_ofport) + ',' + ofports - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], 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): @@ -532,14 +534,15 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): ofports = ','.join(map(str, ldm.get_compute_ofports().values())) ofports = str(ldm.get_csnat_ofport()) + ',' + ofports ip_subnet = subnet_info['cidr'] - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], ofports)) + + # 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): @@ -592,32 +595,39 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): dl_vlan=local_vlan, dl_dst=ovsport.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()) - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], 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: # removed port is a distributed router interface - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - proto='ip', dl_vlan=local_vlan, - nw_dst=ip_subnet) + 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 # ports available on this agent anymore self.local_dvr_map.pop(sub_uuid, None) - self.tun_br.delete_flows(table=constants.DVR_PROCESS, - dl_vlan=local_vlan, - proto='arp', - nw_dst=subnet_info['gateway_ip']) + 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) ovsport.remove_subnet(sub_uuid) self.tun_br.delete_flows(table=constants.DVR_PROCESS, @@ -649,7 +659,7 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): 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'] # first remove this vm port rule self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, dl_vlan=local_vlan, @@ -658,33 +668,29 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # If there is a csnat port on this agent, preserve # the local_dvr_map state ofports = str(ldm.get_csnat_ofport()) + ',' + ofports - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], 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: - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], - 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 - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet) + 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) @@ -708,22 +714,23 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, dl_vlan=local_vlan, dl_dst=ovsport.get_mac()) - ofports = ','.join(map(str, ldm.get_compute_ofports().values())) + ip_version = subnet_info['ip_version'] if ofports: - self.int_br.add_flow(table=constants.DVR_TO_SRC_MAC, - priority=2, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet, - actions="strip_vlan,mod_dl_src:%s," - " output:%s" % - (subnet_info['gateway_mac'], 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: - self.int_br.delete_flows(table=constants.DVR_TO_SRC_MAC, - proto='ip', - dl_vlan=local_vlan, - nw_dst=ip_subnet) + 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 @@ -732,6 +739,27 @@ class OVSDVRNeutronAgent(dvr_rpc.DVRAgentRpcApiMixin): # release port state self.local_ports.pop(port.vif_id, None) + def _get_flow_args_by_version(self, ip_version, table, + vlan, subnet, priority, actions): + """ + Get flow args for DVR by IP version. + priority and actions are optional to support both add_flows + and delete_flows + """ + args = {'table': table, + 'dl_vlan': vlan} + if ip_version == 4: + args['proto'] = 'ip' + args['nw_dst'] = subnet + else: + args['proto'] = 'ipv6' + args['ipv6_dst'] = subnet + if priority: + args['priority'] = priority + if actions: + args['actions'] = actions + return args + def unbind_port_from_dvr(self, vif_port, local_vlan_id): if not self.in_distributed_mode(): return diff --git a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py index b098b710b..e84653252 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py @@ -219,6 +219,7 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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', @@ -241,8 +242,14 @@ class TestOvsNeutronAgent(base.BaseTestCase): self.assertTrue(add_flow_tun_fn.called) self.assertTrue(delete_flows_int_fn.called) - def _test_port_bound_for_dvr(self, device_owner): + 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): @@ -253,8 +260,9 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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', + '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', @@ -274,24 +282,96 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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) - self.assertTrue(add_flow_tun_fn.called) - self.assertTrue(add_flow_int_fn.called) + 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") + 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() @@ -306,6 +386,7 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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', @@ -329,7 +410,19 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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): @@ -339,8 +432,9 @@ class TestOvsNeutronAgent(base.BaseTestCase): return_value=str(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', + 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', @@ -374,11 +468,58 @@ class TestOvsNeutronAgent(base.BaseTestCase): delete_flows_int_fn, delete_flows_tun_fn): self.agent.treat_devices_removed([self._port.vif_id]) - self.assertTrue(delete_flows_int_fn.called) - self.assertTrue(delete_flows_tun_fn.called) - - def _test_treat_devices_removed_for_dvr(self, device_owner): + 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): @@ -388,8 +529,9 @@ class TestOvsNeutronAgent(base.BaseTestCase): return_value=str(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', + 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', @@ -427,18 +569,45 @@ class TestOvsNeutronAgent(base.BaseTestCase): update_dev_down_fn, delete_flows_int_fn): self.agent.treat_devices_removed([self._compute_port.vif_id]) - self.assertTrue(delete_flows_int_fn.called) + 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") + 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() @@ -453,6 +622,7 @@ class TestOvsNeutronAgent(base.BaseTestCase): 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',