From 75832ea45a4a14ded26fc488d6eae80a30fcde24 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 18 Dec 2014 09:11:14 -0800 Subject: [PATCH] refactor l3-agent to include dvr.py Creation of a dvr.py file to hold dvr related classes/data/methods. Change-Id: I7b59ca18f13b583b404be61558224384bc2db2c5 Partially-Implements: bp/restructure-l3-agent --- neutron/agent/l3/agent.py | 415 +------------------------- neutron/agent/l3/dvr.py | 444 ++++++++++++++++++++++++++++ neutron/agent/l3/router_info.py | 10 +- neutron/tests/unit/test_l3_agent.py | 15 +- 4 files changed, 464 insertions(+), 420 deletions(-) create mode 100644 neutron/agent/l3/dvr.py diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 84f96f772..c00a03915 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -27,15 +27,14 @@ from oslo.utils import importutils from oslo.utils import timeutils from neutron.agent.common import config +from neutron.agent.l3 import dvr from neutron.agent.l3 import event_observers from neutron.agent.l3 import ha -from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import router_info from neutron.agent.l3 import router_processing_queue as queue from neutron.agent.linux import external_process from neutron.agent.linux import interface from neutron.agent.linux import ip_lib -from neutron.agent.linux import iptables_manager from neutron.agent.linux import ra from neutron.agent import rpc as agent_rpc from neutron.common import config as common_config @@ -65,18 +64,6 @@ LOG = logging.getLogger(__name__) NS_PREFIX = 'qrouter-' INTERNAL_DEV_PREFIX = 'qr-' EXTERNAL_DEV_PREFIX = 'qg-' -SNAT_INT_DEV_PREFIX = 'sg-' -FIP_NS_PREFIX = 'fip-' -SNAT_NS_PREFIX = 'snat-' -FIP_2_ROUTER_DEV_PREFIX = 'fpr-' -ROUTER_2_FIP_DEV_PREFIX = 'rfp-' -FIP_EXT_DEV_PREFIX = 'fg-' -FIP_LL_SUBNET = '169.254.30.0/23' -# Route Table index for FIPs -FIP_RT_TBL = 16 -# Rule priority range for FIPs -FIP_PR_START = 32768 -FIP_PR_END = FIP_PR_START + 40000 RPC_LOOP_INTERVAL = 1 FLOATING_IP_CIDR_SUFFIX = '/32' @@ -142,6 +129,7 @@ class L3PluginApi(object): class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, ha.AgentMixin, + dvr.AgentMixin, manager.Manager): """Manager for L3NatAgent @@ -272,14 +260,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self._clean_stale_namespaces = self.conf.use_namespaces - # dvr data - self.agent_gateway_port = None - self.fip_ns_subscribers = set() - self.local_subnets = lla.LinkLocalAllocator( - os.path.join(self.conf.state_path, 'fip-linklocal-networks'), - FIP_LL_SUBNET) - self.fip_priorities = set(range(FIP_PR_START, FIP_PR_END)) - self._queue = queue.RouterProcessingQueue() self.event_observers = event_observers.L3EventObservers() super(L3NATAgent, self).__init__(conf=self.conf) @@ -287,15 +267,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self.target_ex_net_id = None self.use_ipv6 = ipv6_utils.is_enabled() - def _fip_ns_subscribe(self, router_id): - is_first = (len(self.fip_ns_subscribers) == 0) - self.fip_ns_subscribers.add(router_id) - return is_first - - def _fip_ns_unsubscribe(self, router_id): - self.fip_ns_subscribers.discard(router_id) - return len(self.fip_ns_subscribers) == 0 - def _check_config_params(self): """Check items in configuration files. @@ -324,7 +295,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, host_namespaces = root_ip.get_namespaces(self.root_helper) return set(ns for ns in host_namespaces if (ns.startswith(NS_PREFIX) - or ns.startswith(SNAT_NS_PREFIX))) + or ns.startswith(dvr.SNAT_NS_PREFIX))) except RuntimeError: LOG.exception(_LE('RuntimeError in obtaining router list ' 'for namespace cleanup.')) @@ -358,9 +329,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, def _destroy_namespace(self, ns): if ns.startswith(NS_PREFIX): self._destroy_router_namespace(ns) - elif ns.startswith(FIP_NS_PREFIX): + elif ns.startswith(dvr.FIP_NS_PREFIX): self._destroy_fip_namespace(ns) - elif ns.startswith(SNAT_NS_PREFIX): + elif ns.startswith(dvr.SNAT_NS_PREFIX): self._destroy_snat_namespace(ns) def _delete_namespace(self, ns_ip, ns): @@ -369,40 +340,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, except RuntimeError: LOG.exception(_LE('Failed trying to delete namespace: %s'), ns) - def _destroy_snat_namespace(self, ns): - ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=ns) - # delete internal interfaces - for d in ns_ip.get_devices(exclude_loopback=True): - if d.name.startswith(SNAT_INT_DEV_PREFIX): - LOG.debug('Unplugging DVR device %s', d.name) - self.driver.unplug(d.name, namespace=ns, - prefix=SNAT_INT_DEV_PREFIX) - - # TODO(mrsmith): delete ext-gw-port - LOG.debug('DVR: destroy snat ns: %s', ns) - if self.conf.router_delete_namespaces: - self._delete_namespace(ns_ip, ns) - - def _destroy_fip_namespace(self, ns): - ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=ns) - for d in ns_ip.get_devices(exclude_loopback=True): - if d.name.startswith(FIP_2_ROUTER_DEV_PREFIX): - # internal link between IRs and FIP NS - ns_ip.del_veth(d.name) - elif d.name.startswith(FIP_EXT_DEV_PREFIX): - # single port from FIP NS to br-ext - # TODO(carl) Where does the port get deleted? - LOG.debug('DVR: unplug: %s', d.name) - self.driver.unplug(d.name, - bridge=self.conf.external_network_bridge, - namespace=ns, - prefix=FIP_EXT_DEV_PREFIX) - LOG.debug('DVR: destroy fip ns: %s', ns) - # TODO(mrsmith): add LOG warn if fip count != 0 - if self.conf.router_delete_namespaces: - self._delete_namespace(ns_ip, ns) - self.agent_gateway_port = None - def _destroy_router_namespace(self, ns): router_id = self.get_router_id(ns) ra.disable_ipv6_ra(router_id, ns, self.root_helper) @@ -414,7 +351,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, # device is on default bridge self.driver.unplug(d.name, namespace=ns, prefix=INTERNAL_DEV_PREFIX) - elif d.name.startswith(ROUTER_2_FIP_DEV_PREFIX): + elif d.name.startswith(dvr.ROUTER_2_FIP_DEV_PREFIX): ns_ip.del_veth(d.name) elif d.name.startswith(EXTERNAL_DEV_PREFIX): self.driver.unplug(d.name, @@ -563,22 +500,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, pm = self._get_metadata_proxy_process_manager(router_id, ns_name) pm.disable() - def _set_subnet_arp_info(self, ri, port): - """Set ARP info retrieved from Plugin for existing ports.""" - if 'id' not in port['subnet'] or not ri.router['distributed']: - return - subnet_id = port['subnet']['id'] - subnet_ports = ( - self.plugin_rpc.get_ports_by_subnet(self.context, - subnet_id)) - - for p in subnet_ports: - if p['device_owner'] not in l3_constants.ROUTER_INTERFACE_OWNERS: - for fixed_ip in p['fixed_ips']: - self._update_arp_entry(ri, fixed_ip['ip_address'], - p['mac_address'], - subnet_id, 'add') - def _set_subnet_info(self, port): ips = port['fixed_ips'] if not ips: @@ -783,29 +704,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, break iptables_manager.apply() - def _handle_router_fip_nat_rules(self, ri, interface_name, action): - """Configures NAT rules for Floating IPs for DVR. - - Remove all the rules. This is safe because if - use_namespaces is set as False then the agent can - only configure one router, otherwise each router's - NAT rules will be in their own namespace. - """ - ri.iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') - ri.iptables_manager.ipv4['nat'].empty_chain('snat') - - # Add back the jump to float-snat - ri.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') - - # And add them back if the action is add_rules - if action == 'add_rules' and interface_name: - rule = ('POSTROUTING', '! -i %(interface_name)s ' - '! -o %(interface_name)s -m conntrack ! ' - '--ctstate DNAT -j ACCEPT' % - {'interface_name': interface_name}) - ri.iptables_manager.ipv4['nat'].add_rule(*rule) - ri.iptables_manager.apply() - def process_router_floating_ip_nat_rules(self, ri): """Configure NAT rules for the router's floating IPs. @@ -867,7 +765,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, if ri.router['distributed']: # Special Handling for DVR - update FIP namespace # and ri.namespace to handle DVR based FIP - self.floating_ip_added_dist(ri, fip) + self.floating_ip_added_dist(ri, fip, ip_cidr) else: # As GARP is processed in a distinct thread the call below # won't raise an exception to be handled. @@ -954,55 +852,18 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, eventlet.spawn_n(self._arping, ns_name, interface_name, ip_address, distributed) - def get_internal_port(self, ri, subnet_id): - """Return internal router port based on subnet_id.""" - router_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) - for port in router_ports: - fips = port['fixed_ips'] - for f in fips: - if f['subnet_id'] == subnet_id: - return port - def get_internal_device_name(self, port_id): return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] def get_external_device_name(self, port_id): return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] - def get_fip_ext_device_name(self, port_id): - return (FIP_EXT_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] - - def get_rtr_int_device_name(self, router_id): - return (ROUTER_2_FIP_DEV_PREFIX + router_id)[:self.driver.DEV_NAME_LEN] - - def get_fip_int_device_name(self, router_id): - return (FIP_2_ROUTER_DEV_PREFIX + router_id)[:self.driver.DEV_NAME_LEN] - - def get_snat_int_device_name(self, port_id): - return (SNAT_INT_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] - - def get_fip_ns_name(self, ext_net_id): - return (FIP_NS_PREFIX + ext_net_id) - def get_ns_name(self, router_id): return (NS_PREFIX + router_id) def get_router_id(self, ns_name): return ns_name[len(NS_PREFIX):] - def get_snat_ns_name(self, router_id): - return (SNAT_NS_PREFIX + router_id) - - def get_snat_interfaces(self, ri): - return ri.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) - - def get_gw_port_host(self, router): - host = router.get('gw_port_host') - if not host: - LOG.debug("gw_port_host missing from router: %s", - router['id']) - return host - def get_floating_ips(self, ri): """Filter Floating IPs to be hosted on this agent.""" floating_ips = ri.router.get(l3_constants.FLOATINGIP_KEY, []) @@ -1010,40 +871,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, floating_ips = [i for i in floating_ips if i['host'] == self.host] return floating_ips - def _map_internal_interfaces(self, ri, int_port, snat_ports): - """Return the SNAT port for the given internal interface port.""" - fixed_ip = int_port['fixed_ips'][0] - subnet_id = fixed_ip['subnet_id'] - match_port = [p for p in snat_ports if - p['fixed_ips'][0]['subnet_id'] == subnet_id] - if match_port: - return match_port[0] - else: - LOG.error(_LE('DVR: no map match_port found!')) - - def _create_dvr_gateway(self, ri, ex_gw_port, gw_interface_name, - snat_ports): - """Create SNAT namespace.""" - snat_ns_name = self.get_snat_ns_name(ri.router['id']) - self._create_namespace(snat_ns_name) - # connect snat_ports to br_int from SNAT namespace - for port in snat_ports: - # create interface_name - self._set_subnet_info(port) - interface_name = self.get_snat_int_device_name(port['id']) - self._internal_network_added(snat_ns_name, port['network_id'], - port['id'], port['ip_cidr'], - port['mac_address'], interface_name, - SNAT_INT_DEV_PREFIX) - self._external_gateway_added(ri, ex_gw_port, gw_interface_name, - snat_ns_name, preserve_ips=[]) - ri.snat_iptables_manager = iptables_manager.IptablesManager( - root_helper=self.root_helper, - namespace=snat_ns_name, - use_ipv6=self.use_ipv6) - # kicks the FW Agent to add rules for the snat namespace - self.process_router_add(ri) - def external_gateway_added(self, ri, ex_gw_port, interface_name): if ri.router['distributed']: ip_wrapr = ip_lib.IPWrapper(self.root_helper, namespace=ri.ns_name) @@ -1125,40 +952,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self._send_gratuitous_arp_packet(ns_name, interface_name, ip_address) - def agent_gateway_added(self, ns_name, ex_gw_port, - interface_name): - """Add Floating IP gateway port to FIP namespace.""" - if not ip_lib.device_exists(interface_name, - root_helper=self.root_helper, - namespace=ns_name): - self.driver.plug(ex_gw_port['network_id'], - ex_gw_port['id'], interface_name, - ex_gw_port['mac_address'], - bridge=self.conf.external_network_bridge, - namespace=ns_name, - prefix=FIP_EXT_DEV_PREFIX) - - self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']], - namespace=ns_name) - ip_address = ex_gw_port['ip_cidr'].split('/')[0] - self._send_gratuitous_arp_packet(ns_name, interface_name, ip_address) - - gw_ip = ex_gw_port['subnet']['gateway_ip'] - if gw_ip: - ipd = ip_lib.IPDevice(interface_name, self.root_helper, - namespace=ns_name) - ipd.route.add_gateway(gw_ip) - - cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name] - ip_wrapper = ip_lib.IPWrapper(self.root_helper, namespace=ns_name) - ip_wrapper.netns.execute(cmd, check_exit_code=False) - - def internal_ns_interface_added(self, ip_cidr, - interface_name, ns_name): - ip_wrapper = ip_lib.IPWrapper(self.root_helper, namespace=ns_name) - ip_wrapper.netns.execute(['ip', 'addr', 'add', - ip_cidr, 'dev', interface_name]) - def external_gateway_removed(self, ri, ex_gw_port, interface_name): if ri.router['distributed']: self.process_router_floating_ip_nat_rules(ri) @@ -1212,32 +1005,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, (interface_name, ex_gw_ip))] return rules - def _snat_redirect_add(self, ri, gateway, sn_port, sn_int): - """Adds rules and routes for SNAT redirection.""" - try: - snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value - ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper, - namespace=ri.ns_name) - ns_ipd.route.add_gateway(gateway, table=snat_idx) - ns_ipr.add_rule_from(sn_port['ip_cidr'], snat_idx, snat_idx) - ns_ipr.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.' - 'send_redirects=0' % sn_int]) - except Exception: - LOG.exception(_LE('DVR: error adding redirection logic')) - - def _snat_redirect_remove(self, ri, sn_port, sn_int): - """Removes rules and routes for SNAT redirection.""" - try: - snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value - ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper, - namespace=ri.ns_name) - ns_ipd.route.delete_gateway(table=snat_idx) - ns_ipr.delete_rule_priority(snat_idx) - except Exception: - LOG.exception(_LE('DVR: removed snat failed')) - def _internal_network_added(self, ns_name, network_id, port_id, internal_cidr, mac_address, interface_name, prefix, is_ha=False): @@ -1290,7 +1057,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, sn_port['ip_cidr'], sn_port['mac_address'], interface_name, - SNAT_INT_DEV_PREFIX) + dvr.SNAT_INT_DEV_PREFIX) def internal_network_removed(self, ri, port): port_id = port['id'] @@ -1307,7 +1074,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self.get_snat_int_device_name(snat_port['id']) ) ns_name = self.get_snat_ns_name(ri.router['id']) - prefix = SNAT_INT_DEV_PREFIX + prefix = dvr.SNAT_INT_DEV_PREFIX if ip_lib.device_exists(snat_interface, root_helper=self.root_helper, namespace=ns_name): @@ -1322,128 +1089,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self.driver.unplug(interface_name, namespace=ri.ns_name, prefix=INTERNAL_DEV_PREFIX) - def _create_agent_gateway_port(self, ri, network_id): - """Create Floating IP gateway port. - - Request port creation from Plugin then creates - Floating IP namespace and adds gateway port. - """ - self.agent_gateway_port = ( - self.plugin_rpc.get_agent_gateway_port( - self.context, network_id)) - if 'subnet' not in self.agent_gateway_port: - LOG.error(_LE('Missing subnet/agent_gateway_port')) - return - self._set_subnet_info(self.agent_gateway_port) - - # add fip-namespace and agent_gateway_port - fip_ns_name = ( - self.get_fip_ns_name(str(network_id))) - self._create_namespace(fip_ns_name) - ri.fip_iptables_manager = iptables_manager.IptablesManager( - root_helper=self.root_helper, namespace=fip_ns_name, - use_ipv6=self.use_ipv6) - # no connection tracking needed in fip namespace - ri.fip_iptables_manager.ipv4['raw'].add_rule('PREROUTING', - '-j CT --notrack') - ri.fip_iptables_manager.apply() - interface_name = ( - self.get_fip_ext_device_name(self.agent_gateway_port['id'])) - self.agent_gateway_added(fip_ns_name, self.agent_gateway_port, - interface_name) - - def create_rtr_2_fip_link(self, ri, network_id): - """Create interface between router and Floating IP namespace.""" - rtr_2_fip_name = self.get_rtr_int_device_name(ri.router_id) - fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) - fip_ns_name = self.get_fip_ns_name(str(network_id)) - - # add link local IP to interface - if ri.rtr_fip_subnet is None: - ri.rtr_fip_subnet = self.local_subnets.allocate(ri.router_id) - rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() - ip_wrapper = ip_lib.IPWrapper(self.root_helper, - namespace=ri.ns_name) - if not ip_lib.device_exists(rtr_2_fip_name, self.root_helper, - namespace=ri.ns_name): - int_dev = ip_wrapper.add_veth(rtr_2_fip_name, - fip_2_rtr_name, fip_ns_name) - self.internal_ns_interface_added(str(rtr_2_fip), - rtr_2_fip_name, ri.ns_name) - self.internal_ns_interface_added(str(fip_2_rtr), - fip_2_rtr_name, fip_ns_name) - int_dev[0].link.set_up() - int_dev[1].link.set_up() - # add default route for the link local interface - device = ip_lib.IPDevice(rtr_2_fip_name, self.root_helper, - namespace=ri.ns_name) - device.route.add_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) - #setup the NAT rules and chains - self._handle_router_fip_nat_rules(ri, rtr_2_fip_name, 'add_rules') - # kicks the FW Agent to add rules for the IR namespace if configured - self.process_router_add(ri) - - def floating_ip_added_dist(self, ri, fip): - """Add floating IP to FIP namespace.""" - floating_ip = fip['floating_ip_address'] - fixed_ip = fip['fixed_ip_address'] - rule_pr = self.fip_priorities.pop() - ri.floating_ips_dict[floating_ip] = rule_pr - fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) - ip_rule = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - ip_rule.add_rule_from(fixed_ip, FIP_RT_TBL, rule_pr) - - #Add routing rule in fip namespace - fip_cidr = str(floating_ip) + FLOATING_IP_CIDR_SUFFIX - fip_ns_name = self.get_fip_ns_name(str(fip['floating_network_id'])) - rtr_2_fip, _ = ri.rtr_fip_subnet.get_pair() - device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, - namespace=fip_ns_name) - device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) - interface_name = ( - self.get_fip_ext_device_name(self.agent_gateway_port['id'])) - self._send_gratuitous_arp_packet(fip_ns_name, - interface_name, floating_ip, - distributed=True) - # update internal structures - ri.dist_fip_count = ri.dist_fip_count + 1 - - def floating_ip_removed_dist(self, ri, fip_cidr): - """Remove floating IP from FIP namespace.""" - floating_ip = fip_cidr.split('/')[0] - rtr_2_fip_name = self.get_rtr_int_device_name(ri.router_id) - fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) - rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() - fip_ns_name = self.get_fip_ns_name(str(self._fetch_external_net_id())) - ip_rule_rtr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) - if floating_ip in ri.floating_ips_dict: - rule_pr = ri.floating_ips_dict[floating_ip] - #TODO(rajeev): Handle else case - exception/log? - else: - rule_pr = None - - ip_rule_rtr.delete_rule_priority(rule_pr) - self.fip_priorities.add(rule_pr) - device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, - 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 - ri.dist_fip_count = ri.dist_fip_count - 1 - if ri.dist_fip_count == 0: - #remove default route entry - device = ip_lib.IPDevice(rtr_2_fip_name, self.root_helper, - namespace=ri.ns_name) - ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=fip_ns_name) - device.route.delete_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) - self.local_subnets.release(ri.router_id) - ri.rtr_fip_subnet = None - ns_ip.del_veth(fip_2_rtr_name) - is_last = self._fip_ns_unsubscribe(ri.router_id) - # clean up fip-namespace if this is the last FIP - if is_last: - self._destroy_fip_namespace(fip_ns_name) - def floating_forward_rules(self, floating_ip, fixed_ip): return [('PREROUTING', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)), @@ -1460,48 +1105,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, action=queue.DELETE_ROUTER) self._queue.add(update) - def _update_arp_entry(self, ri, ip, mac, subnet_id, operation): - """Add or delete arp entry into router namespace for the subnet.""" - port = self.get_internal_port(ri, subnet_id) - # update arp entry only if the subnet is attached to the router - if port: - ip_cidr = str(ip) + '/32' - try: - # TODO(mrsmith): optimize the calls below for bulk calls - net = netaddr.IPNetwork(ip_cidr) - interface_name = self.get_internal_device_name(port['id']) - device = ip_lib.IPDevice(interface_name, self.root_helper, - namespace=ri.ns_name) - if operation == 'add': - device.neigh.add(net.version, ip, mac) - elif operation == 'delete': - device.neigh.delete(net.version, ip, mac) - except Exception: - LOG.exception(_LE("DVR: Failed updating arp entry")) - self.fullsync = True - - def add_arp_entry(self, context, payload): - """Add arp entry into router namespace. Called from RPC.""" - arp_table = payload['arp_table'] - router_id = payload['router_id'] - ip = arp_table['ip_address'] - mac = arp_table['mac_address'] - subnet_id = arp_table['subnet_id'] - ri = self.router_info.get(router_id) - if ri: - self._update_arp_entry(ri, ip, mac, subnet_id, 'add') - - def del_arp_entry(self, context, payload): - """Delete arp entry from router namespace. Called from RPC.""" - arp_table = payload['arp_table'] - router_id = payload['router_id'] - ip = arp_table['ip_address'] - mac = arp_table['mac_address'] - subnet_id = arp_table['subnet_id'] - ri = self.router_info.get(router_id) - if ri: - self._update_arp_entry(ri, ip, mac, subnet_id, 'delete') - def routers_updated(self, context, routers): """Deal with routers modification and creation RPC message.""" LOG.debug('Got routers updated notification :%s', routers) diff --git a/neutron/agent/l3/dvr.py b/neutron/agent/l3/dvr.py new file mode 100644 index 000000000..db88b5a84 --- /dev/null +++ b/neutron/agent/l3/dvr.py @@ -0,0 +1,444 @@ +# Copyright (c) 2014 Openstack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import netaddr +import os + +from neutron.agent.l3 import link_local_allocator as lla +from neutron.agent.linux import ip_lib +from neutron.agent.linux import iptables_manager +from neutron.common import constants as l3_constants +from neutron.i18n import _LE +from neutron.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + +SNAT_INT_DEV_PREFIX = 'sg-' +FIP_EXT_DEV_PREFIX = 'fg-' +FIP_NS_PREFIX = 'fip-' +SNAT_NS_PREFIX = 'snat-' +FIP_2_ROUTER_DEV_PREFIX = 'fpr-' +ROUTER_2_FIP_DEV_PREFIX = 'rfp-' +FIP_LL_SUBNET = '169.254.30.0/23' +# Rule priority range for FIPs +FIP_PR_START = 32768 +FIP_PR_END = FIP_PR_START + 40000 +# Route Table index for FIPs +FIP_RT_TBL = 16 + + +class RouterMixin(object): + def __init__(self): + self.snat_ports = [] + self.floating_ips_dict = {} + + self.snat_iptables_manager = None + # DVR Data + # Linklocal subnet for router and floating IP namespace link + self.rtr_fip_subnet = None + self.dist_fip_count = 0 + + +class AgentMixin(object): + def __init__(self, host): + # dvr data + self.agent_gateway_port = None + self.fip_ns_subscribers = set() + self.local_subnets = lla.LinkLocalAllocator( + os.path.join(self.conf.state_path, 'fip-linklocal-networks'), + FIP_LL_SUBNET) + self.fip_priorities = set(range(FIP_PR_START, + FIP_PR_END)) + + super(AgentMixin, self).__init__(host) + + def _fip_ns_subscribe(self, router_id): + is_first = (len(self.fip_ns_subscribers) == 0) + self.fip_ns_subscribers.add(router_id) + return is_first + + def _fip_ns_unsubscribe(self, router_id): + self.fip_ns_subscribers.discard(router_id) + return len(self.fip_ns_subscribers) == 0 + + def _destroy_snat_namespace(self, ns): + ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=ns) + # delete internal interfaces + for d in ns_ip.get_devices(exclude_loopback=True): + if d.name.startswith(SNAT_INT_DEV_PREFIX): + LOG.debug('Unplugging DVR device %s', d.name) + self.driver.unplug(d.name, namespace=ns, + prefix=SNAT_INT_DEV_PREFIX) + + # TODO(mrsmith): delete ext-gw-port + LOG.debug('DVR: destroy snat ns: %s', ns) + if self.conf.router_delete_namespaces: + self._delete_namespace(ns_ip, ns) + + def _destroy_fip_namespace(self, ns): + ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=ns) + for d in ns_ip.get_devices(exclude_loopback=True): + if d.name.startswith(FIP_2_ROUTER_DEV_PREFIX): + # internal link between IRs and FIP NS + ns_ip.del_veth(d.name) + elif d.name.startswith(FIP_EXT_DEV_PREFIX): + # single port from FIP NS to br-ext + # TODO(carl) Where does the port get deleted? + LOG.debug('DVR: unplug: %s', d.name) + self.driver.unplug(d.name, + bridge=self.conf.external_network_bridge, + namespace=ns, + prefix=FIP_EXT_DEV_PREFIX) + LOG.debug('DVR: destroy fip ns: %s', ns) + # TODO(mrsmith): add LOG warn if fip count != 0 + if self.conf.router_delete_namespaces: + self._delete_namespace(ns_ip, ns) + self.agent_gateway_port = None + + def _set_subnet_arp_info(self, ri, port): + """Set ARP info retrieved from Plugin for existing ports.""" + if 'id' not in port['subnet'] or not ri.router['distributed']: + return + subnet_id = port['subnet']['id'] + subnet_ports = ( + self.plugin_rpc.get_ports_by_subnet(self.context, + subnet_id)) + + for p in subnet_ports: + if p['device_owner'] not in l3_constants.ROUTER_INTERFACE_OWNERS: + for fixed_ip in p['fixed_ips']: + self._update_arp_entry(ri, fixed_ip['ip_address'], + p['mac_address'], + subnet_id, 'add') + + def get_internal_port(self, ri, subnet_id): + """Return internal router port based on subnet_id.""" + router_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) + for port in router_ports: + fips = port['fixed_ips'] + for f in fips: + if f['subnet_id'] == subnet_id: + return port + + def get_fip_ext_device_name(self, port_id): + return (FIP_EXT_DEV_PREFIX + + port_id)[:self.driver.DEV_NAME_LEN] + + def get_rtr_int_device_name(self, router_id): + return (ROUTER_2_FIP_DEV_PREFIX + + router_id)[:self.driver.DEV_NAME_LEN] + + def get_fip_int_device_name(self, router_id): + return (FIP_2_ROUTER_DEV_PREFIX + + router_id)[:self.driver.DEV_NAME_LEN] + + def get_snat_int_device_name(self, port_id): + return (SNAT_INT_DEV_PREFIX + + port_id)[:self.driver.DEV_NAME_LEN] + + def get_fip_ns_name(self, ext_net_id): + return (FIP_NS_PREFIX + ext_net_id) + + def get_snat_ns_name(self, router_id): + return (SNAT_NS_PREFIX + router_id) + + def get_snat_interfaces(self, ri): + return ri.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) + + def get_gw_port_host(self, router): + host = router.get('gw_port_host') + if not host: + LOG.debug("gw_port_host missing from router: %s", + router['id']) + return host + + def _map_internal_interfaces(self, ri, int_port, snat_ports): + """Return the SNAT port for the given internal interface port.""" + fixed_ip = int_port['fixed_ips'][0] + subnet_id = fixed_ip['subnet_id'] + match_port = [p for p in snat_ports if + p['fixed_ips'][0]['subnet_id'] == subnet_id] + if match_port: + return match_port[0] + else: + LOG.error(_LE('DVR: no map match_port found!')) + + def internal_ns_interface_added(self, ip_cidr, + interface_name, ns_name): + ip_wrapper = ip_lib.IPWrapper(self.root_helper, namespace=ns_name) + ip_wrapper.netns.execute(['ip', 'addr', 'add', + ip_cidr, 'dev', interface_name]) + + def _handle_router_fip_nat_rules(self, ri, interface_name, action): + """Configures NAT rules for Floating IPs for DVR. + + Remove all the rules. This is safe because if + use_namespaces is set as False then the agent can + only configure one router, otherwise each router's + NAT rules will be in their own namespace. + """ + ri.iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') + ri.iptables_manager.ipv4['nat'].empty_chain('snat') + + # Add back the jump to float-snat + ri.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') + + # And add them back if the action is add_rules + if action == 'add_rules' and interface_name: + rule = ('POSTROUTING', '! -i %(interface_name)s ' + '! -o %(interface_name)s -m conntrack ! ' + '--ctstate DNAT -j ACCEPT' % + {'interface_name': interface_name}) + ri.iptables_manager.ipv4['nat'].add_rule(*rule) + ri.iptables_manager.apply() + + def _create_dvr_gateway(self, ri, ex_gw_port, gw_interface_name, + snat_ports): + """Create SNAT namespace.""" + snat_ns_name = self.get_snat_ns_name(ri.router['id']) + self._create_namespace(snat_ns_name) + # connect snat_ports to br_int from SNAT namespace + for port in snat_ports: + # create interface_name + self._set_subnet_info(port) + interface_name = self.get_snat_int_device_name(port['id']) + self._internal_network_added(snat_ns_name, port['network_id'], + port['id'], port['ip_cidr'], + port['mac_address'], interface_name, + SNAT_INT_DEV_PREFIX) + self._external_gateway_added(ri, ex_gw_port, gw_interface_name, + snat_ns_name, preserve_ips=[]) + ri.snat_iptables_manager = iptables_manager.IptablesManager( + root_helper=self.root_helper, + namespace=snat_ns_name, + use_ipv6=self.use_ipv6) + # kicks the FW Agent to add rules for the snat namespace + self.process_router_add(ri) + + def agent_gateway_added(self, ns_name, ex_gw_port, + interface_name): + """Add Floating IP gateway port to FIP namespace.""" + if not ip_lib.device_exists(interface_name, + root_helper=self.root_helper, + namespace=ns_name): + self.driver.plug(ex_gw_port['network_id'], + ex_gw_port['id'], interface_name, + ex_gw_port['mac_address'], + bridge=self.conf.external_network_bridge, + namespace=ns_name, + prefix=FIP_EXT_DEV_PREFIX) + + self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']], + namespace=ns_name) + ip_address = ex_gw_port['ip_cidr'].split('/')[0] + self._send_gratuitous_arp_packet(ns_name, interface_name, ip_address) + + gw_ip = ex_gw_port['subnet']['gateway_ip'] + if gw_ip: + ipd = ip_lib.IPDevice(interface_name, self.root_helper, + namespace=ns_name) + ipd.route.add_gateway(gw_ip) + + cmd = ['sysctl', '-w', 'net.ipv4.conf.%s.proxy_arp=1' % interface_name] + ip_wrapper = ip_lib.IPWrapper(self.root_helper, namespace=ns_name) + ip_wrapper.netns.execute(cmd, check_exit_code=False) + + def _create_agent_gateway_port(self, ri, network_id): + """Create Floating IP gateway port. + + Request port creation from Plugin then creates + Floating IP namespace and adds gateway port. + """ + self.agent_gateway_port = ( + self.plugin_rpc.get_agent_gateway_port( + self.context, network_id)) + if 'subnet' not in self.agent_gateway_port: + LOG.error(_LE('Missing subnet/agent_gateway_port')) + return + self._set_subnet_info(self.agent_gateway_port) + + # add fip-namespace and agent_gateway_port + fip_ns_name = ( + self.get_fip_ns_name(str(network_id))) + self._create_namespace(fip_ns_name) + ri.fip_iptables_manager = iptables_manager.IptablesManager( + root_helper=self.root_helper, namespace=fip_ns_name, + use_ipv6=self.use_ipv6) + # no connection tracking needed in fip namespace + ri.fip_iptables_manager.ipv4['raw'].add_rule('PREROUTING', + '-j CT --notrack') + ri.fip_iptables_manager.apply() + interface_name = ( + self.get_fip_ext_device_name(self.agent_gateway_port['id'])) + self.agent_gateway_added(fip_ns_name, self.agent_gateway_port, + interface_name) + + def create_rtr_2_fip_link(self, ri, network_id): + """Create interface between router and Floating IP namespace.""" + rtr_2_fip_name = self.get_rtr_int_device_name(ri.router_id) + fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) + fip_ns_name = self.get_fip_ns_name(str(network_id)) + + # add link local IP to interface + if ri.rtr_fip_subnet is None: + ri.rtr_fip_subnet = self.local_subnets.allocate(ri.router_id) + rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() + ip_wrapper = ip_lib.IPWrapper(self.root_helper, + namespace=ri.ns_name) + if not ip_lib.device_exists(rtr_2_fip_name, self.root_helper, + namespace=ri.ns_name): + int_dev = ip_wrapper.add_veth(rtr_2_fip_name, + fip_2_rtr_name, fip_ns_name) + self.internal_ns_interface_added(str(rtr_2_fip), + rtr_2_fip_name, ri.ns_name) + self.internal_ns_interface_added(str(fip_2_rtr), + fip_2_rtr_name, fip_ns_name) + int_dev[0].link.set_up() + int_dev[1].link.set_up() + # add default route for the link local interface + device = ip_lib.IPDevice(rtr_2_fip_name, self.root_helper, + namespace=ri.ns_name) + device.route.add_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL) + #setup the NAT rules and chains + self._handle_router_fip_nat_rules(ri, rtr_2_fip_name, 'add_rules') + # kicks the FW Agent to add rules for the IR namespace if configured + self.process_router_add(ri) + + def floating_ip_added_dist(self, ri, 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_priorities.pop() + ri.floating_ips_dict[floating_ip] = rule_pr + fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) + ip_rule = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) + ip_rule.add_rule_from(fixed_ip, FIP_RT_TBL, rule_pr) + #Add routing rule in fip namespace + fip_ns_name = self.get_fip_ns_name(str(fip['floating_network_id'])) + rtr_2_fip, _ = ri.rtr_fip_subnet.get_pair() + device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, + namespace=fip_ns_name) + device.route.add_route(fip_cidr, str(rtr_2_fip.ip)) + interface_name = ( + self.get_fip_ext_device_name(self.agent_gateway_port['id'])) + self._send_gratuitous_arp_packet(fip_ns_name, + interface_name, floating_ip, + distributed=True) + # update internal structures + ri.dist_fip_count = ri.dist_fip_count + 1 + + def floating_ip_removed_dist(self, ri, fip_cidr): + """Remove floating IP from FIP namespace.""" + floating_ip = fip_cidr.split('/')[0] + rtr_2_fip_name = self.get_rtr_int_device_name(ri.router_id) + fip_2_rtr_name = self.get_fip_int_device_name(ri.router_id) + rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair() + fip_ns_name = self.get_fip_ns_name(str(self._fetch_external_net_id())) + ip_rule_rtr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) + if floating_ip in ri.floating_ips_dict: + rule_pr = ri.floating_ips_dict[floating_ip] + #TODO(rajeev): Handle else case - exception/log? + else: + rule_pr = None + + ip_rule_rtr.delete_rule_priority(rule_pr) + self.fip_priorities.add(rule_pr) + device = ip_lib.IPDevice(fip_2_rtr_name, self.root_helper, + 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 + ri.dist_fip_count = ri.dist_fip_count - 1 + if ri.dist_fip_count == 0: + #remove default route entry + device = ip_lib.IPDevice(rtr_2_fip_name, self.root_helper, + namespace=ri.ns_name) + ns_ip = ip_lib.IPWrapper(self.root_helper, namespace=fip_ns_name) + device.route.delete_gateway(str(fip_2_rtr.ip), + table=FIP_RT_TBL) + self.local_subnets.release(ri.router_id) + ri.rtr_fip_subnet = None + ns_ip.del_veth(fip_2_rtr_name) + is_last = self._fip_ns_unsubscribe(ri.router_id) + # clean up fip-namespace if this is the last FIP + if is_last: + self._destroy_fip_namespace(fip_ns_name) + + def _snat_redirect_add(self, ri, gateway, sn_port, sn_int): + """Adds rules and routes for SNAT redirection.""" + try: + snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value + ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) + ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper, + namespace=ri.ns_name) + ns_ipd.route.add_gateway(gateway, table=snat_idx) + ns_ipr.add_rule_from(sn_port['ip_cidr'], snat_idx, snat_idx) + ns_ipr.netns.execute(['sysctl', '-w', 'net.ipv4.conf.%s.' + 'send_redirects=0' % sn_int]) + except Exception: + LOG.exception(_LE('DVR: error adding redirection logic')) + + def _snat_redirect_remove(self, ri, sn_port, sn_int): + """Removes rules and routes for SNAT redirection.""" + try: + snat_idx = netaddr.IPNetwork(sn_port['ip_cidr']).value + ns_ipr = ip_lib.IpRule(self.root_helper, namespace=ri.ns_name) + ns_ipd = ip_lib.IPDevice(sn_int, self.root_helper, + namespace=ri.ns_name) + ns_ipd.route.delete_gateway(table=snat_idx) + ns_ipr.delete_rule_priority(snat_idx) + except Exception: + LOG.exception(_LE('DVR: removed snat failed')) + + def _update_arp_entry(self, ri, ip, mac, subnet_id, operation): + """Add or delete arp entry into router namespace for the subnet.""" + port = self.get_internal_port(ri, subnet_id) + # update arp entry only if the subnet is attached to the router + if port: + ip_cidr = str(ip) + '/32' + try: + # TODO(mrsmith): optimize the calls below for bulk calls + net = netaddr.IPNetwork(ip_cidr) + interface_name = self.get_internal_device_name(port['id']) + device = ip_lib.IPDevice(interface_name, self.root_helper, + namespace=ri.ns_name) + if operation == 'add': + device.neigh.add(net.version, ip, mac) + elif operation == 'delete': + device.neigh.delete(net.version, ip, mac) + except Exception: + LOG.exception(_LE("DVR: Failed updating arp entry")) + self.fullsync = True + + def add_arp_entry(self, context, payload): + """Add arp entry into router namespace. Called from RPC.""" + arp_table = payload['arp_table'] + router_id = payload['router_id'] + ip = arp_table['ip_address'] + mac = arp_table['mac_address'] + subnet_id = arp_table['subnet_id'] + ri = self.router_info.get(router_id) + if ri: + self._update_arp_entry(ri, ip, mac, subnet_id, 'add') + + def del_arp_entry(self, context, payload): + """Delete arp entry from router namespace. Called from RPC.""" + arp_table = payload['arp_table'] + router_id = payload['router_id'] + ip = arp_table['ip_address'] + mac = arp_table['mac_address'] + subnet_id = arp_table['subnet_id'] + ri = self.router_info.get(router_id) + if ri: + self._update_arp_entry(ri, ip, mac, subnet_id, 'delete') diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index 2999ed853..16827d53e 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -12,11 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. +from neutron.agent.l3 import dvr from neutron.agent.l3 import ha from neutron.agent.linux import iptables_manager -class RouterInfo(ha.RouterMixin): +class RouterInfo(dvr.RouterMixin, ha.RouterMixin): def __init__(self, router_id, root_helper, router, use_ipv6=False, ns_name=None): @@ -25,9 +26,7 @@ class RouterInfo(ha.RouterMixin): self._snat_enabled = None self._snat_action = None self.internal_ports = [] - self.snat_ports = [] self.floating_ips = set() - self.floating_ips_dict = {} self.root_helper = root_helper # Invoke the setter for establishing initial SNAT action self.router = router @@ -36,12 +35,7 @@ class RouterInfo(ha.RouterMixin): root_helper=root_helper, use_ipv6=use_ipv6, namespace=self.ns_name) - self.snat_iptables_manager = None self.routes = [] - # DVR Data - # Linklocal subnet for router and floating IP namespace link - self.rtr_fip_subnet = None - self.dist_fip_count = 0 super(RouterInfo, self).__init__() diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index 4a8944d86..f459682cf 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -24,6 +24,7 @@ from testtools import matchers from neutron.agent.common import config as agent_config from neutron.agent.l3 import agent as l3_agent +from neutron.agent.l3 import dvr from neutron.agent.l3 import ha from neutron.agent.l3 import link_local_allocator as lla from neutron.agent.l3 import router_info as l3router @@ -364,7 +365,7 @@ class TestBasicRouterOperations(base.BaseTestCase): sn_port['ip_cidr'], sn_port['mac_address'], agent.get_snat_int_device_name(sn_port['id']), - l3_agent.SNAT_INT_DEV_PREFIX) + dvr.SNAT_INT_DEV_PREFIX) def test_agent_add_internal_network(self): self._test_internal_network_action('add') @@ -1807,7 +1808,7 @@ vrrp_instance VR_1 { good_namespace_list = [l3_agent.NS_PREFIX + r['id'] for r in router_list] - good_namespace_list += [l3_agent.SNAT_NS_PREFIX + r['id'] + good_namespace_list += [dvr.SNAT_NS_PREFIX + r['id'] for r in router_list] self.mock_ip.get_namespaces.return_value = (stale_namespace_list + good_namespace_list + @@ -1842,7 +1843,7 @@ vrrp_instance VR_1 { self.conf.set_override('router_id', None) stale_namespaces = [l3_agent.NS_PREFIX + 'foo', l3_agent.NS_PREFIX + 'bar', - l3_agent.SNAT_NS_PREFIX + 'foo'] + dvr.SNAT_NS_PREFIX + 'foo'] other_namespaces = ['unknown'] self._cleanup_namespace_test(stale_namespaces, @@ -1853,7 +1854,7 @@ vrrp_instance VR_1 { self.conf.set_override('router_id', None) stale_namespaces = [l3_agent.NS_PREFIX + 'cccc', l3_agent.NS_PREFIX + 'eeeee', - l3_agent.SNAT_NS_PREFIX + 'fffff'] + dvr.SNAT_NS_PREFIX + 'fffff'] router_list = [{'id': 'foo', 'distributed': False}, {'id': 'aaaa', 'distributed': False}] other_namespaces = ['qdhcp-aabbcc', 'unknown'] @@ -1989,7 +1990,9 @@ vrrp_instance VR_1 { 'port_id': _uuid()} agent.agent_gateway_port = agent_gw_port ri.rtr_fip_subnet = lla.LinkLocalAddressPair('169.254.30.42/31') - agent.floating_ip_added_dist(ri, fip) + ip_cidr = str(fip['floating_ip_address']) + ( + l3_agent.FLOATING_IP_CIDR_SUFFIX) + agent.floating_ip_added_dist(ri, fip, ip_cidr) self.mock_rule.add_rule_from.assert_called_with('192.168.0.1', 16, FIP_PRI) # TODO(mrsmith): add more asserts @@ -2152,7 +2155,7 @@ vrrp_instance VR_1 { self.mock_ip.del_veth.assert_called_once_with( agent.get_fip_int_device_name(ri.router['id'])) self.mock_ip_dev.route.delete_gateway.assert_called_once_with( - str(fip_to_rtr.ip), table=l3_agent.FIP_RT_TBL) + str(fip_to_rtr.ip), table=dvr.FIP_RT_TBL) self.assertEqual(ri.dist_fip_count, 0) self.assertEqual(len(agent.fip_ns_subscribers), 0) -- 2.45.2