def _get_fixed_ip_rule_priority(self, namespace, fip): iprule = ip_lib.IPRule(namespace) lines = iprule.rule._as_root([4], ['show']).splitlines() for line in lines: if fip in line: info = iprule.rule._parse_line(4, line) return info['priority']
def floating_ip_added_dist(self, fip, fip_cidr): """Add floating IP to FIP namespace.""" floating_ip = fip['floating_ip_address'] fixed_ip = fip['fixed_ip_address'] rule_pr = self.fip_ns.allocate_rule_priority(floating_ip) self.floating_ips_dict[floating_ip] = rule_pr fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.add(ip=fixed_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) #Add routing rule in fip namespace fip_ns_name = self.fip_ns.get_name() if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, _ = self.rtr_fip_subnet.get_pair() device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) interface_name = (self.fip_ns.get_ext_device_name( self.fip_ns.agent_gateway_port['id'])) ip_lib.send_ip_addr_adv_notif(fip_ns_name, interface_name, floating_ip, self.agent_conf) # update internal structures self.dist_fip_count = self.dist_fip_count + 1
def floating_ip_removed_dist(self, fip_cidr): """Remove floating IP from FIP namespace.""" floating_ip = fip_cidr.split('/')[0] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() fip_ns_name = self.fip_ns.get_name() if floating_ip in self.floating_ips_dict: rule_pr = self.floating_ips_dict[floating_ip] ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.delete(ip=floating_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) self.fip_ns.deallocate_rule_priority(floating_ip) #TODO(rajeev): Handle else case - exception/log? device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) # check if this is the last FIP for this router self.dist_fip_count = self.dist_fip_count - 1 if self.dist_fip_count == 0: #remove default route entry device = ip_lib.IPDevice(rtr_2_fip_name, namespace=self.ns_name) ns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) device.route.delete_gateway(str(fip_2_rtr.ip), table=dvr_fip_ns.FIP_RT_TBL) self.fip_ns.local_subnets.release(self.router_id) self.rtr_fip_subnet = None ns_ip.del_veth(fip_2_rtr_name) self.fip_ns.unsubscribe(self.router_id)
def _delete_interface_routing_rule_in_router_ns(self, router_port): ip_rule = ip_lib.IPRule(namespace=self.ns_name) for subnet in router_port['subnets']: rtr_port_cidr = subnet['cidr'] ip_rule.rule.delete(ip=rtr_port_cidr, table=dvr_fip_ns.FIP_RT_TBL, priority=dvr_fip_ns.FAST_PATH_EXIT_PR)
def test_dvr_router_gateway_redirect_cleanup_on_agent_restart(self): """Test to validate the router namespace gateway redirect rule cleanup. This test checks for the non existence of the gateway redirect rules in the router namespace after the agent restarts while the gateway is removed for the router. """ self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info() router1 = self.manage_router(self.agent, router_info) self._assert_snat_namespace_exists(router1) self.assertTrue(self._namespace_exists(router1.ns_name)) restarted_agent = neutron_l3_agent.L3NATAgentWithStateReport( self.agent.host, self.agent.conf) router1.router['gw_port'] = "" router1.router['gw_port_host'] = "" router1.router['external_gateway_info'] = "" restarted_router = self.manage_router(restarted_agent, router1.router) self.assertTrue(self._namespace_exists(restarted_router.ns_name)) ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_4) ip6_rules_list = ns_ipr.rule.list_rules(lib_constants.IP_VERSION_6) # Just make sure the basic set of rules are there in the router # namespace self.assertEqual(3, len(ip4_rules_list)) self.assertEqual(2, len(ip6_rules_list))
def _add_rtr_ext_route_rule_to_route_table(self, ri, fip_2_rtr, fip_2_rtr_name): """Creates external route table and adds routing rules.""" # TODO(Swami): Rename the _get_snat_idx function to some # generic name that can be used for SNAT and FIP rt_tbl_index = ri._get_snat_idx(fip_2_rtr) interface_name = self.get_ext_device_name( self.agent_gateway_port['id']) try: # The lock is used to make sure another thread doesn't call to # update the gateway route before we are done initializing things. with self._fip_port_lock(interface_name): self._update_gateway_route(self.agent_gateway_port, interface_name, tbl_index=rt_tbl_index) except Exception: # If an exception occurs at this point, then it is # good to unsubscribe this external network so that # the next call will trigger the interface to be plugged. # We reraise the exception in order to resync the router. with excutils.save_and_reraise_exception(): self.unsubscribe(self.agent_gateway_port['network_id']) # Reset the fip count so that the create_rtr_2_fip_link # is called again in this context ri.dist_fip_count = 0 LOG.exception(_LE('DVR: Gateway update route in FIP namespace ' 'failed')) # Now add the filter match rule for the table. ip_rule = ip_lib.IPRule(namespace=self.get_name()) ip_rule.rule.add(ip=str(fip_2_rtr.ip), iif=fip_2_rtr_name, table=rt_tbl_index, priority=rt_tbl_index)
def test_dvr_gateway_move_does_not_remove_redirect_rules(self): """Test to validate snat redirect rules not cleared with snat move.""" self.agent.conf.agent_mode = 'dvr_snat' router_info = self.generate_dvr_router_info(enable_snat=True) router_info[l3_constants.FLOATINGIP_KEY] = [] router_info[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = [] router1 = self.manage_router(self.agent, router_info) router1.router['gw_port_host'] = "" self.agent._process_updated_router(router1.router) router_updated = self.agent.router_info[router1.router['id']] self.assertTrue(self._namespace_exists(router_updated.ns_name)) ns_ipr = ip_lib.IPRule(namespace=router1.ns_name) ip4_rules_list = ns_ipr.rule.list_rules(l3_constants.IP_VERSION_4) self.assertEqual(5, len(ip4_rules_list)) # IPRule list should have 5 entries. # Three entries from 'default', 'main' and 'local' table. # The remaining 2 is for the two router interfaces(csnat ports). default_rules_list_count = 0 interface_rules_list_count = 0 for ip_rule in ip4_rules_list: tbl_index = ip_rule['table'] if tbl_index in ['local', 'default', 'main']: default_rules_list_count = default_rules_list_count + 1 else: interface_rules_list_count = interface_rules_list_count + 1 self.assertEqual(3, default_rules_list_count) self.assertEqual(2, interface_rules_list_count)
def _add_floating_ip_rule(self, floating_ip, fixed_ip): rule_pr = self.fip_ns.allocate_rule_priority(floating_ip) self.floating_ips_dict[floating_ip] = rule_pr ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.add(ip=fixed_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr)
def _remove_floating_ip_rule(self, floating_ip): if floating_ip in self.floating_ips_dict: rule_pr = self.floating_ips_dict[floating_ip] ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.delete(ip=floating_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) self.fip_ns.deallocate_rule_priority(floating_ip)
def _fixed_ip_rule_exists(self, namespace, ip): iprule = ip_lib.IPRule(namespace) lines = iprule.rule._as_root([4], ['show']).splitlines() for line in lines: if ip in line: info = iprule.rule._parse_line(4, line) if info['from'] == ip: return True return False
def get_fip_table_indexes(self, ip_version): ns_ipr = ip_lib.IPRule(namespace=self.get_name()) ip_rules_list = ns_ipr.rule.list_rules(ip_version) tbl_index_list = [] for ip_rule in ip_rules_list: tbl_index = ip_rule['table'] if tbl_index in ['local', 'default', 'main']: continue tbl_index_list.append(tbl_index) return tbl_index_list
def floating_ip_removed_dist(self, fip_cidr): """Remove floating IP from FIP namespace.""" floating_ip = fip_cidr.split('/')[0] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() fip_ns_name = self.fip_ns.get_name() if floating_ip in self.floating_ips_dict: rule_pr = self.floating_ips_dict[floating_ip] ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.delete(ip=floating_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) self.fip_ns.deallocate_rule_priority(floating_ip) #TODO(rajeev): Handle else case - exception/log? device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) # check if this is the last FIP for this router self.dist_fip_count = self.dist_fip_count - 1 if self.dist_fip_count == 0: #remove default route entry device = ip_lib.IPDevice(rtr_2_fip_name, namespace=self.ns_name) ns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) device.route.delete_gateway(str(fip_2_rtr.ip), table=dvr_fip_ns.FIP_RT_TBL) self.fip_ns.local_subnets.release(self.router_id) self.rtr_fip_subnet = None ns_ip.del_veth(fip_2_rtr_name) is_last = self.fip_ns.unsubscribe(self.router_id) if is_last: # TODO(Carl) I can't help but think that another router could # come in and want to start using this namespace while this is # destroying it. The two could end up conflicting on # creating/destroying interfaces and such. I think I'd like a # semaphore to sync creation/deletion of this namespace. # NOTE (Swami): Since we are deleting the namespace here we # should be able to delete the floatingip agent gateway port # for the provided external net since we don't need it anymore. if self.fip_ns.agent_gateway_port: LOG.debug( 'Removed last floatingip, so requesting the ' 'server to delete Floatingip Agent Gateway port:' '%s', self.fip_ns.agent_gateway_port) self.agent.plugin_rpc.delete_agent_gateway_port( self.agent.context, self.fip_ns.agent_gateway_port['network_id']) self.fip_ns.delete() self.fip_ns = None
def _snat_redirect_remove(self, gateway, sn_port, sn_int): """Removes rules and routes for SNAT redirection.""" try: ip_cidr = sn_port['ip_cidr'] snat_idx = self._get_snat_idx(ip_cidr) ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name) ns_ipd.route.delete_gateway(gateway, table=snat_idx) ns_ipr.rule.delete(ip_cidr, snat_idx, snat_idx) except Exception: LOG.exception(_LE('DVR: removed snat failed'))
def test_metadata_proxy_exit_clear_ip_rule(self): df_metadata_service.environment_setup() ip_rule = ip_lib.IPRule().rule rules = ip_rule.list_rules(4) rules_source = [r['from'] for r in rules if 'from' in r] self.assertIn(self.metadata_ip, rules_source) df_metadata_service.environment_destroy() self.isTornDown = True rules = ip_rule.list_rules(4) rules_source = [r['from'] for r in rules if 'from' in r] self.assertNotIn(self.metadata_ip, rules_source)
def delete_rtr_2_fip_link(self, ri): """Delete the interface between router and FloatingIP namespace.""" LOG.debug("Delete FIP link interfaces for router: %s", ri.router_id) rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id) fip_2_rtr_name = self.get_int_device_name(ri.router_id) fip_ns_name = self.get_name() # remove default route entry if ri.rtr_fip_subnet is None: # see if there is a local subnet in the cache ri.rtr_fip_subnet = self.local_subnets.lookup(ri.router_id) if ri.rtr_fip_subnet: rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() device = ip_lib.IPDevice(rtr_2_fip_name, namespace=ri.ns_name) if device.exists(): device.route.delete_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) if self.agent_gateway_port: interface_name = self.get_ext_device_name( self.agent_gateway_port['id']) fg_device = ip_lib.IPDevice( interface_name, namespace=fip_ns_name) if fg_device.exists(): # Remove the fip namespace rules and routes associated to # fpr interface route table. tbl_index = ri._get_snat_idx(fip_2_rtr) fip_rt_rule = ip_lib.IPRule(namespace=fip_ns_name) # Flush the table fg_device.route.flush(lib_constants.IP_VERSION_4, table=tbl_index) fg_device.route.flush(lib_constants.IP_VERSION_6, table=tbl_index) # Remove the rule lookup # /0 addresses for IPv4 and IPv6 are used to pass # IP protocol version information based on a # link-local address IP version. Using any of those # is equivalent to using 'from all' for iproute2. rule_ip = lib_constants.IP_ANY[fip_2_rtr.ip.version] fip_rt_rule.rule.delete(ip=rule_ip, iif=fip_2_rtr_name, table=tbl_index, priority=tbl_index) self.local_subnets.release(ri.router_id) ri.rtr_fip_subnet = None # Check for namespace before deleting the device if not self.destroyed: fns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) if fns_ip.device(fip_2_rtr_name).exists(): fns_ip.del_veth(fip_2_rtr_name)
def floating_ip_removed_dist(self, fip_cidr): """Remove floating IP from FIP namespace.""" floating_ip = fip_cidr.split('/')[0] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() fip_ns_name = self.fip_ns.get_name() self._remove_floating_ip_rule(floating_ip) device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) # check if this is the last FIP for this router self.dist_fip_count = self.dist_fip_count - 1 if self.dist_fip_count == 0: #remove default route entry device = ip_lib.IPDevice(rtr_2_fip_name, namespace=self.ns_name) ns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) device.route.delete_gateway(str(fip_2_rtr.ip), table=dvr_fip_ns.FIP_RT_TBL) if self.fip_ns.agent_gateway_port: interface_name = self.fip_ns.get_ext_device_name( self.fip_ns.agent_gateway_port['id']) fg_device = ip_lib.IPDevice(interface_name, namespace=fip_ns_name) if fg_device.exists(): # Remove the fip namespace rules and routes associated to # fpr interface route table. tbl_index = self._get_snat_idx(fip_2_rtr) fip_rt_rule = ip_lib.IPRule(namespace=fip_ns_name) # Flush the table fg_device.route.flush(lib_constants.IP_VERSION_4, table=tbl_index) fg_device.route.flush(lib_constants.IP_VERSION_6, table=tbl_index) # Remove the rule lookup # IP is ignored in delete, but we still require it # for getting the ip_version. fip_rt_rule.rule.delete(ip=fip_2_rtr.ip, iif=fip_2_rtr_name, table=tbl_index, priority=tbl_index) self.fip_ns.local_subnets.release(self.router_id) self.rtr_fip_subnet = None ns_ip.del_veth(fip_2_rtr_name)
def delete_rtr_2_fip_link(self, ri): """Delete the interface between router and FloatingIP namespace.""" LOG.debug("Delete FIP link interfaces for router: %s", ri.router_id) rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id) fip_2_rtr_name = self.get_int_device_name(ri.router_id) fip_ns_name = self.get_name() # remove default route entry if ri.rtr_fip_subnet is None: # see if there is a local subnet in the cache ri.rtr_fip_subnet = self.local_subnets.lookup(ri.router_id) if ri.rtr_fip_subnet: rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() device = ip_lib.IPDevice(rtr_2_fip_name, namespace=ri.ns_name) if device.exists(): device.route.delete_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) if self.agent_gateway_port: interface_name = self.get_ext_device_name( self.agent_gateway_port['id']) fg_device = ip_lib.IPDevice(interface_name, namespace=fip_ns_name) if fg_device.exists(): # Remove the fip namespace rules and routes associated to # fpr interface route table. tbl_index = ri._get_snat_idx(fip_2_rtr) fip_rt_rule = ip_lib.IPRule(namespace=fip_ns_name) # Flush the table fg_device.route.flush(lib_constants.IP_VERSION_4, table=tbl_index) fg_device.route.flush(lib_constants.IP_VERSION_6, table=tbl_index) # Remove the rule lookup # IP is ignored in delete, but we still require it # for getting the ip_version. fip_rt_rule.rule.delete(ip=fip_2_rtr.ip, iif=fip_2_rtr_name, table=tbl_index, priority=tbl_index) self.local_subnets.release(ri.router_id) ri.rtr_fip_subnet = None # Check for namespace before deleting the device if not self.destroyed: fns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) if fns_ip.device(fip_2_rtr_name).exists(): fns_ip.del_veth(fip_2_rtr_name)
def _snat_redirect_add(self, gateway, sn_port, sn_int): """Adds rules and routes for SNAT redirection.""" try: ip_cidr = sn_port['ip_cidr'] snat_idx = self._get_snat_idx(ip_cidr) ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name) ns_ipwrapr = ip_lib.IPWrapper(namespace=self.ns_name) ns_ipd.route.add_gateway(gateway, table=snat_idx) ns_ipr.rule.add(ip_cidr, snat_idx, snat_idx) ns_ipwrapr.netns.execute([ 'sysctl', '-w', 'net.ipv4.conf.%s.' 'send_redirects=0' % sn_int ]) except Exception: LOG.exception(_LE('DVR: error adding redirection logic'))
def floating_ip_removed_dist(self, fip_cidr): """Remove floating IP from FIP namespace.""" floating_ip = fip_cidr.split('/')[0] rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(self.router_id) fip_2_rtr_name = self.fip_ns.get_int_device_name(self.router_id) if self.rtr_fip_subnet is None: self.rtr_fip_subnet = self.fip_ns.local_subnets.allocate( self.router_id) rtr_2_fip, fip_2_rtr = self.rtr_fip_subnet.get_pair() fip_ns_name = self.fip_ns.get_name() if floating_ip in self.floating_ips_dict: rule_pr = self.floating_ips_dict[floating_ip] ip_rule = ip_lib.IPRule(namespace=self.ns_name) ip_rule.rule.delete(ip=floating_ip, table=dvr_fip_ns.FIP_RT_TBL, priority=rule_pr) self.fip_ns.deallocate_rule_priority(rule_pr) #TODO(rajeev): Handle else case - exception/log? device = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name) device.route.delete_route(fip_cidr, str(rtr_2_fip.ip)) # check if this is the last FIP for this router self.dist_fip_count = self.dist_fip_count - 1 if self.dist_fip_count == 0: #remove default route entry device = ip_lib.IPDevice(rtr_2_fip_name, namespace=self.ns_name) ns_ip = ip_lib.IPWrapper(namespace=fip_ns_name) device.route.delete_gateway(str(fip_2_rtr.ip), table=dvr_fip_ns.FIP_RT_TBL) self.fip_ns.local_subnets.release(self.router_id) self.rtr_fip_subnet = None ns_ip.del_veth(fip_2_rtr_name) is_last = self.fip_ns.unsubscribe(self.router_id) if is_last: # TODO(Carl) I can't help but think that another router could # come in and want to start using this namespace while this is # destroying it. The two could end up conflicting on # creating/destroying interfaces and such. I think I'd like a # semaphore to sync creation/deletion of this namespace. self.fip_ns.delete() self.fip_ns = None
def _snat_redirect_modify(self, gateway, sn_port, sn_int, is_add): """Adds or removes rules and routes for SNAT redirection.""" try: ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(sn_int, namespace=self.ns_name) if is_add: ns_ipwrapr = ip_lib.IPWrapper(namespace=self.ns_name) for port_fixed_ip in sn_port['fixed_ips']: # Find the first gateway IP address matching this IP version port_ip_addr = port_fixed_ip['ip_address'] port_ip_vers = netaddr.IPAddress(port_ip_addr).version for gw_fixed_ip in gateway['fixed_ips']: gw_ip_addr = gw_fixed_ip['ip_address'] if netaddr.IPAddress(gw_ip_addr).version == port_ip_vers: sn_port_cidr = common_utils.ip_to_cidr( port_ip_addr, port_fixed_ip['prefixlen']) snat_idx = self._get_snat_idx(sn_port_cidr) if is_add: ns_ipd.route.add_gateway(gw_ip_addr, table=snat_idx) ns_ipr.rule.add(ip=sn_port_cidr, table=snat_idx, priority=snat_idx) ns_ipwrapr.netns.execute([ 'sysctl', '-w', 'net.ipv4.conf.%s.send_redirects=0' % sn_int ]) else: self._delete_gateway_device_if_exists( ns_ipd, gw_ip_addr, snat_idx) ns_ipr.rule.delete(ip=sn_port_cidr, table=snat_idx, priority=snat_idx) break except Exception: if is_add: exc = _LE('DVR: error adding redirection logic') else: exc = _LE('DVR: snat remove failed to clear the rule ' 'and device') LOG.exception(exc)
def setUp(self): super(ListIpRulesTestCase, self).setUp() self.namespace = 'ns_test-' + uuidutils.generate_uuid() self.ns = priv_ip_lib.create_netns(self.namespace) self.ip_rule = ip_lib.IPRule(namespace=self.namespace) self.addCleanup(self._remove_ns)
def gateway_redirect_cleanup(self, rtr_interface): ns_ipr = ip_lib.IPRule(namespace=self.ns_name) ns_ipd = ip_lib.IPDevice(rtr_interface, namespace=self.ns_name) self._stale_ip_rule_cleanup(ns_ipr, ns_ipd, lib_constants.IP_VERSION_4) self._stale_ip_rule_cleanup(ns_ipr, ns_ipd, lib_constants.IP_VERSION_6)
def test_rules_lifecycle(self): PRIORITY = 32768 TABLE = 16 attr = self.generate_device_details() device = self.manage_device(attr) test_cases = { constants.IP_VERSION_4: [ { 'ip': '1.1.1.1', 'to': '8.8.8.0/24' }, { 'ip': '1.1.1.1', 'iif': device.name, 'to': '7.7.7.0/24' } ], constants.IP_VERSION_6: [ { 'ip': 'abcd::1', 'to': '1234::/64' }, { 'ip': 'abcd::1', 'iif': device.name, 'to': '4567::/64' } ] } expected_rules = { constants.IP_VERSION_4: [ { 'from': '1.1.1.1', 'to': '8.8.8.0/24', 'priority': str(PRIORITY), 'table': str(TABLE), 'type': 'unicast' }, { 'from': '0.0.0.0/0', 'to': '7.7.7.0/24', 'iif': device.name, 'priority': str(PRIORITY), 'table': str(TABLE), 'type': 'unicast' } ], constants.IP_VERSION_6: [ { 'from': 'abcd::1', 'to': '1234::/64', 'priority': str(PRIORITY), 'table': str(TABLE), 'type': 'unicast' }, { 'from': '::/0', 'to': '4567::/64', 'iif': device.name, 'priority': str(PRIORITY), 'table': str(TABLE), 'type': 'unicast', } ] } ip_rule = ip_lib.IPRule(namespace=device.namespace) for ip_version, test_case in test_cases.items(): for rule in test_case: ip_rule.rule.add(table=TABLE, priority=PRIORITY, **rule) rules = ip_lib.list_ip_rules(ip_rule.namespace, ip_version) for expected_rule in expected_rules[ip_version]: self.assertIn(expected_rule, rules) for rule in test_case: ip_rule.rule.delete(table=TABLE, priority=PRIORITY, **rule) rules = priv_ip_lib.list_ip_rules(ip_rule.namespace, ip_version) for expected_rule in expected_rules[ip_version]: self.assertNotIn(expected_rule, rules)