From 4555e2adb7ae3055226ed7ddcc1512aa9c15d409 Mon Sep 17 00:00:00 2001 From: Carl Baldwin Date: Mon, 23 Feb 2015 23:01:47 +0000 Subject: [PATCH] Move external port processing to router classes Change-Id: I5f0682c68c9b820393f3b5e9eb126391a06da775 Partially-Implements: bp/restructure-l3-agent --- neutron/agent/l3/agent.py | 277 +----------------- neutron/agent/l3/dvr.py | 23 -- neutron/agent/l3/dvr_router.py | 160 +++++++++- neutron/agent/l3/ha_router.py | 31 +- neutron/agent/l3/router_info.py | 175 ++++++++++- .../tests/functional/agent/test_l3_agent.py | 16 +- neutron/tests/unit/test_l3_agent.py | 124 ++++---- 7 files changed, 406 insertions(+), 400 deletions(-) diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 692ab9d16..2b9197241 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -14,7 +14,6 @@ # import eventlet -import netaddr from oslo_config import cfg from oslo_log import log as logging import oslo_messaging @@ -331,52 +330,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, self.event_observers.notify( adv_svc.AdvancedService.after_router_removed, ri) - def _process_external_gateway(self, ri): - ex_gw_port = ri.get_ex_gw_port() - ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or - ri.ex_gw_port and ri.ex_gw_port['id']) - - interface_name = None - if ex_gw_port_id: - interface_name = self.get_external_device_name(ex_gw_port_id) - if ex_gw_port: - def _gateway_ports_equal(port1, port2): - def _get_filtered_dict(d, ignore): - return dict((k, v) for k, v in d.iteritems() - if k not in ignore) - - keys_to_ignore = set(['binding:host_id']) - port1_filtered = _get_filtered_dict(port1, keys_to_ignore) - port2_filtered = _get_filtered_dict(port2, keys_to_ignore) - return port1_filtered == port2_filtered - - ri._set_subnet_info(ex_gw_port) - if not ri.ex_gw_port: - self.external_gateway_added(ri, ex_gw_port, interface_name) - elif not _gateway_ports_equal(ex_gw_port, ri.ex_gw_port): - self.external_gateway_updated(ri, ex_gw_port, interface_name) - elif not ex_gw_port and ri.ex_gw_port: - self.external_gateway_removed(ri, ri.ex_gw_port, interface_name) - - existing_devices = ri._get_existing_devices() - stale_devs = [dev for dev in existing_devices - if dev.startswith(EXTERNAL_DEV_PREFIX) - and dev != interface_name] - for stale_dev in stale_devs: - LOG.debug('Deleting stale external router device: %s', - stale_dev) - self.driver.unplug(stale_dev, - bridge=self.conf.external_network_bridge, - namespace=ri.ns_name, - prefix=EXTERNAL_DEV_PREFIX) - - # Process SNAT rules for external gateway - if (not ri.router['distributed'] or - ex_gw_port and ri.get_gw_port_host() == self.host): - ri.perform_snat_action(self._handle_router_snat_rules, - interface_name) - - def _update_fip_statuses(self, ri, existing_floating_ips, fip_statuses): + def update_fip_statuses(self, ri, existing_floating_ips, fip_statuses): # Identify floating IPs which were disabled ri.floating_ips = set(fip_statuses.keys()) for fip_id in existing_floating_ips - ri.floating_ips: @@ -393,36 +347,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, else: ri.disable_keepalived() - def _process_external(self, ri): - existing_floating_ips = ri.floating_ips - try: - with ri.iptables_manager.defer_apply(): - self._process_external_gateway(ri) - ex_gw_port = ri.get_ex_gw_port() - # TODO(Carl) Return after setting existing_floating_ips and - # still call _update_fip_statuses? - if not ex_gw_port: - return - - # Process SNAT/DNAT rules and addresses for floating IPs - if ri.router['distributed']: - self.create_dvr_fip_interfaces(ri, ex_gw_port) - ri.process_snat_dnat_for_fip() - - # Once NAT rules for floating IPs are safely in place - # configure their addresses on the external gateway port - interface_name = self._get_external_device_interface_name( - ri, ex_gw_port) - fip_statuses = ri.configure_fip_addresses(interface_name) - - except (n_exc.FloatingIpSetupException, - n_exc.IpTablesApplyException) as e: - # All floating IPs must be put in error state - LOG.exception(e) - fip_statuses = ri.put_fips_in_error_state() - - self._update_fip_statuses(ri, existing_floating_ips, fip_statuses) - @common_utils.exception_logger() def process_router(self, ri): # TODO(mrsmith) - we shouldn't need to check here @@ -433,7 +357,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, ri.fip_ns = self.get_fip_ns(ex_gw_port['network_id']) ri.fip_ns.scan_fip_ports(ri) ri._process_internal_ports() - self._process_external(ri) + ri.process_external(self) # Process static routes for router ri.routes_updated() @@ -445,203 +369,6 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, ri.snat_ports = ri.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) ri.enable_snat = ri.router.get('enable_snat') - def _handle_router_snat_rules(self, ri, ex_gw_port, - interface_name, action): - # 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 SNAT rules will be in their own namespace - if not ri.router['distributed']: - iptables_manager = ri.iptables_manager - elif ri.snat_iptables_manager: - iptables_manager = ri.snat_iptables_manager - else: - LOG.debug("DVR router: no snat rules to be handled") - return - - iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') - iptables_manager.ipv4['nat'].empty_chain('snat') - - if not ri.router['distributed']: - # Add back the jump to float-snat - 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 ex_gw_port: - # ex_gw_port should not be None in this case - # NAT rules are added only if ex_gw_port has an IPv4 address - for ip_addr in ex_gw_port['fixed_ips']: - ex_gw_ip = ip_addr['ip_address'] - if netaddr.IPAddress(ex_gw_ip).version == 4: - rules = self.external_gateway_nat_rules(ex_gw_ip, - interface_name) - for rule in rules: - iptables_manager.ipv4['nat'].add_rule(*rule) - break - iptables_manager.apply() - - def create_dvr_fip_interfaces(self, ri, ex_gw_port): - floating_ips = ri.get_floating_ips() - fip_agent_port = ri.get_floating_agent_gw_interface( - ex_gw_port['network_id']) - LOG.debug("FloatingIP agent gateway port received from the plugin: " - "%s", fip_agent_port) - if floating_ips: - is_first = ri.fip_ns.subscribe(ri.router_id) - if is_first and fip_agent_port: - if 'subnet' not in fip_agent_port: - LOG.error(_LE('Missing subnet/agent_gateway_port')) - else: - ri._set_subnet_info(fip_agent_port) - ri.fip_ns.create_gateway_port(fip_agent_port) - - if ri.fip_ns.agent_gateway_port and floating_ips: - if ri.dist_fip_count == 0: - ri.fip_ns.create_rtr_2_fip_link(ri) - - # kicks the FW Agent to add rules for the IR namespace if - # configured - self.process_router_add(ri) - - def _get_external_device_interface_name(self, ri, ex_gw_port): - if ri.router['distributed']: - fip_int = ri.fip_ns.get_int_device_name(ri.router_id) - if ip_lib.device_exists(fip_int, namespace=ri.fip_ns.get_name()): - return ri.fip_ns.get_rtr_ext_device_name(ri.router_id) - else: - return self.get_external_device_name(ex_gw_port['id']) - - def get_external_device_name(self, port_id): - return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] - - def external_gateway_added(self, ri, ex_gw_port, interface_name): - if ri.router['distributed']: - ip_wrapr = ip_lib.IPWrapper(namespace=ri.ns_name) - ip_wrapr.netns.execute(['sysctl', '-w', - 'net.ipv4.conf.all.send_redirects=0']) - snat_ports = ri.get_snat_interfaces() - for p in ri.internal_ports: - gateway = ri._map_internal_interfaces(p, snat_ports) - id_name = ri.get_internal_device_name(p['id']) - if gateway: - ri._snat_redirect_add( - gateway['fixed_ips'][0]['ip_address'], p, id_name) - - if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT and - ri.get_gw_port_host() == self.host): - self._create_dvr_gateway(ri, ex_gw_port, interface_name, - snat_ports) - for port in snat_ports: - for ip in port['fixed_ips']: - ri._update_arp_entry(ip['ip_address'], - port['mac_address'], - ip['subnet_id'], - 'add') - return - - # Compute a list of addresses this router is supposed to have. - # This avoids unnecessarily removing those addresses and - # causing a momentarily network outage. - floating_ips = ri.get_floating_ips() - preserve_ips = [common_utils.ip_to_cidr(ip['floating_ip_address']) - for ip in floating_ips] - - self._external_gateway_added(ri, ex_gw_port, interface_name, - ri.ns_name, preserve_ips) - - if ri.is_ha: - ri._ha_external_gateway_added(ex_gw_port, interface_name) - ri._ha_disable_addressing_on_interface(interface_name) - - def external_gateway_updated(self, ri, ex_gw_port, interface_name): - preserve_ips = [] - if ri.router['distributed']: - if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT and - ri.get_gw_port_host() == self.host): - ns_name = ri.snat_namespace.name - else: - # no centralized SNAT gateway for this node/agent - LOG.debug("not hosting snat for router: %s", ri.router['id']) - return - else: - ns_name = ri.ns_name - floating_ips = ri.get_floating_ips() - preserve_ips = [common_utils.ip_to_cidr(ip['floating_ip_address']) - for ip in floating_ips] - - self._external_gateway_added(ri, ex_gw_port, interface_name, - ns_name, preserve_ips) - - if ri.is_ha: - ri._ha_external_gateway_updated(ex_gw_port, interface_name) - - def _external_gateway_added(self, ri, ex_gw_port, interface_name, - ns_name, preserve_ips): - if not ip_lib.device_exists(interface_name, 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=EXTERNAL_DEV_PREFIX) - - if not ri.is_ha: - self.driver.init_l3( - interface_name, [ex_gw_port['ip_cidr']], namespace=ns_name, - gateway=ex_gw_port['subnet'].get('gateway_ip'), - extra_subnets=ex_gw_port.get('extra_subnets', []), - preserve_ips=preserve_ips) - ip_address = ex_gw_port['ip_cidr'].split('/')[0] - ip_lib.send_gratuitous_arp(ns_name, - interface_name, - ip_address, - self.conf.send_arp_for_ha) - - def external_gateway_removed(self, ri, ex_gw_port, interface_name): - if ri.router['distributed']: - # TODO(Carl) Should this be calling process_snat_dnat_for_fip? - ri.process_floating_ip_nat_rules() - if ri.fip_ns: - to_fip_interface_name = ( - self._get_external_device_interface_name(ri, ex_gw_port)) - ri.process_floating_ip_addresses(to_fip_interface_name) - snat_ports = ri.get_snat_interfaces() - for p in ri.internal_ports: - gateway = self._map_internal_interfaces(ri, p, snat_ports) - internal_interface = ri.get_internal_device_name(p['id']) - ri._snat_redirect_remove(gateway['fixed_ips'][0]['ip_address'], - p, - internal_interface) - - if (self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT - and ri.get_gw_port_host() == self.host): - ns_name = ri.snat_namespace.name - else: - # not hosting agent - no work to do - LOG.debug('DVR: CSNAT not hosted: %s', ex_gw_port) - return - else: - ns_name = ri.ns_name - - if ri.is_ha: - ri._ha_external_gateway_removed(interface_name) - - self.driver.unplug(interface_name, - bridge=self.conf.external_network_bridge, - namespace=ns_name, - prefix=EXTERNAL_DEV_PREFIX) - if ri.router['distributed']: - ri.delete_snat_namespace() - - def external_gateway_nat_rules(self, ex_gw_ip, interface_name): - rules = [('POSTROUTING', '! -i %(interface_name)s ' - '! -o %(interface_name)s -m conntrack ! ' - '--ctstate DNAT -j ACCEPT' % - {'interface_name': interface_name}), - ('snat', '-o %s -j SNAT --to-source %s' % - (interface_name, ex_gw_ip))] - return rules - def router_deleted(self, context, router_id): """Deal with router deletion RPC message.""" LOG.debug('Got router deleted notification for %s', router_id) diff --git a/neutron/agent/l3/dvr.py b/neutron/agent/l3/dvr.py index d6829b63e..64c75f7f3 100644 --- a/neutron/agent/l3/dvr.py +++ b/neutron/agent/l3/dvr.py @@ -18,7 +18,6 @@ from oslo_log import log as logging from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_snat_ns -from neutron.agent.linux import iptables_manager LOG = logging.getLogger(__name__) @@ -58,28 +57,6 @@ class AgentMixin(object): def get_ports_by_subnet(self, subnet_id): return self.plugin_rpc.get_ports_by_subnet(self.context, subnet_id) - def _create_dvr_gateway(self, ri, ex_gw_port, gw_interface_name, - snat_ports): - """Create SNAT namespace.""" - snat_ns = ri.create_snat_namespace() - # connect snat_ports to br_int from SNAT namespace - for port in snat_ports: - # create interface_name - ri._set_subnet_info(port) - interface_name = ri.get_snat_int_device_name(port['id']) - # TODO(Carl) calling private method on router. Will fix soon. - ri._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( - 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 add_arp_entry(self, context, payload): """Add arp entry into router namespace. Called from RPC.""" router_id = payload['router_id'] diff --git a/neutron/agent/l3/dvr_router.py b/neutron/agent/l3/dvr_router.py index 6af1e67ad..0c9e9139f 100644 --- a/neutron/agent/l3/dvr_router.py +++ b/neutron/agent/l3/dvr_router.py @@ -22,6 +22,7 @@ from neutron.agent.l3 import dvr_fip_ns from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import router_info as router from neutron.agent.linux import ip_lib +from neutron.agent.linux import iptables_manager from neutron.common import constants as l3_constants from neutron.common import utils as common_utils from neutron.i18n import _LE @@ -170,13 +171,6 @@ class DvrRouter(router.RouterInfo): self.snat_namespace.create() return self.snat_namespace - def delete_snat_namespace(self): - # TODO(mlavalle): in the near future, this method should contain the - # code in the L3 agent that removes an external gateway for a dvr. The - # first step is to move the deletion of the snat namespace here - self.snat_namespace.delete() - self.snat_namespace = None - def _get_internal_port(self, subnet_id): """Return internal router port based on subnet_id.""" router_ports = self.router.get(l3_constants.INTERFACE_KEY, []) @@ -291,6 +285,12 @@ class DvrRouter(router.RouterInfo): self.router['id']) return host + def _is_this_snat_host(self): + # TODO(Carl) This is a sign that dvr needs two router classes. + mode = self.agent_conf.agent_mode + return (mode == l3_constants.L3_AGENT_MODE_DVR_SNAT + and self.get_gw_port_host() == self.host) + def internal_network_added(self, port): super(DvrRouter, self).internal_network_added(port) @@ -308,10 +308,7 @@ class DvrRouter(router.RouterInfo): port, interface_name) - # TODO(Carl) This is a sign that dvr needs two router classes. - is_this_snat_host = (self.agent_conf.agent_mode == 'dvr_snat' and - self.get_gw_port_host() == self.host) - if not is_this_snat_host: + if not self._is_this_snat_host(): return ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id']) @@ -342,8 +339,9 @@ class DvrRouter(router.RouterInfo): port, interface_name) - is_this_snat_host = (self.agent_conf.agent_mode == 'dvr_snat' and - self.ex_gw_port['binding:host_id'] == self.host) + mode = self.agent_conf.agent_mode + is_this_snat_host = (mode == l3_constants.L3_AGENT_MODE_DVR_SNAT + and self.ex_gw_port['binding:host_id'] == self.host) if not is_this_snat_host: return @@ -364,3 +362,139 @@ class DvrRouter(router.RouterInfo): fip_ports = self.router.get(l3_constants.FLOATINGIP_AGENT_INTF_KEY, []) return next( (p for p in fip_ports if p['network_id'] == ext_net_id), None) + + def get_external_device_interface_name(self, ex_gw_port): + fip_int = self.fip_ns.get_int_device_name(self.router_id) + if ip_lib.device_exists(fip_int, namespace=self.fip_ns.get_name()): + return self.fip_ns.get_rtr_ext_device_name(self.router_id) + + def _create_dvr_gateway(self, ex_gw_port, gw_interface_name, + snat_ports): + """Create SNAT namespace.""" + snat_ns = self.create_snat_namespace() + # 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, + dvr_snat_ns.SNAT_INT_DEV_PREFIX) + self._external_gateway_added(ex_gw_port, gw_interface_name, + snat_ns.name, preserve_ips=[]) + self.snat_iptables_manager = iptables_manager.IptablesManager( + namespace=snat_ns.name, + use_ipv6=self.use_ipv6) + # kicks the FW Agent to add rules for the snat namespace + self.agent.process_router_add(self) + + def external_gateway_added(self, ex_gw_port, interface_name): + # TODO(Carl) Refactor external_gateway_added/updated/removed to use + # super class implementation where possible. Looks like preserve_ips, + # and ns_name are the key differences. + ip_wrapr = ip_lib.IPWrapper(namespace=self.ns_name) + ip_wrapr.netns.execute(['sysctl', '-w', + 'net.ipv4.conf.all.send_redirects=0']) + snat_ports = self.get_snat_interfaces() + for p in self.internal_ports: + gateway = self._map_internal_interfaces(p, snat_ports) + id_name = self.get_internal_device_name(p['id']) + if gateway: + self._snat_redirect_add( + gateway['fixed_ips'][0]['ip_address'], p, id_name) + + if self._is_this_snat_host(): + self._create_dvr_gateway(ex_gw_port, interface_name, snat_ports) + + for port in snat_ports: + for ip in port['fixed_ips']: + self._update_arp_entry(ip['ip_address'], + port['mac_address'], + ip['subnet_id'], + 'add') + + def external_gateway_updated(self, ex_gw_port, interface_name): + if not self._is_this_snat_host(): + # no centralized SNAT gateway for this node/agent + LOG.debug("not hosting snat for router: %s", self.router['id']) + return + + self._external_gateway_added(ex_gw_port, + interface_name, + self.snat_namespace.name, + preserve_ips=[]) + + def external_gateway_removed(self, ex_gw_port, interface_name): + # TODO(Carl) Should this be calling process_snat_dnat_for_fip? + self.process_floating_ip_nat_rules() + if self.fip_ns: + to_fip_interface_name = ( + self.get_external_device_interface_name(ex_gw_port)) + self.process_floating_ip_addresses(to_fip_interface_name) + snat_ports = self.get_snat_interfaces() + for p in self.internal_ports: + gateway = self._map_internal_interfaces(p, snat_ports) + internal_interface = self.get_internal_device_name(p['id']) + self._snat_redirect_remove(gateway['fixed_ips'][0]['ip_address'], + p, + internal_interface) + + if not self._is_this_snat_host(): + # no centralized SNAT gateway for this node/agent + LOG.debug("not hosting snat for router: %s", self.router['id']) + return + + self.driver.unplug(interface_name, + bridge=self.agent_conf.external_network_bridge, + namespace=self.snat_namespace.name, + prefix=router.EXTERNAL_DEV_PREFIX) + + self.snat_namespace.delete() + self.snat_namespace = None + + def _handle_router_snat_rules(self, ex_gw_port, + interface_name, action): + if not self.snat_iptables_manager: + LOG.debug("DVR router: no snat rules to be handled") + return + + with self.snat_iptables_manager.defer_apply(): + self._empty_snat_chains(self.snat_iptables_manager) + + # NOTE DVR doesn't add the jump to float snat like the super class. + + self._add_snat_rules(ex_gw_port, self.snat_iptables_manager, + interface_name, action) + + def perform_snat_action(self, snat_callback, *args): + # NOTE DVR skips this step in a few cases... + if not self.get_ex_gw_port(): + return + if self.get_gw_port_host() != self.host: + return + + super(DvrRouter, self).perform_snat_action(snat_callback, *args) + + def create_dvr_fip_interfaces(self, ex_gw_port): + floating_ips = self.get_floating_ips() + fip_agent_port = self.get_floating_agent_gw_interface( + ex_gw_port['network_id']) + LOG.debug("FloatingIP agent gateway port received from the plugin: " + "%s", fip_agent_port) + if floating_ips: + is_first = self.fip_ns.subscribe(self.router_id) + if is_first and fip_agent_port: + if 'subnet' not in fip_agent_port: + LOG.error(_LE('Missing subnet/agent_gateway_port')) + else: + self._set_subnet_info(fip_agent_port) + self.fip_ns.create_gateway_port(fip_agent_port) + + if self.fip_ns.agent_gateway_port and floating_ips: + if self.dist_fip_count == 0: + self.fip_ns.create_rtr_2_fip_link(self) + + # kicks the FW Agent to add rules for the IR namespace if + # configured + self.agent.process_router_add(self) diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index d28c6c6d6..3fa9504f3 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -165,9 +165,6 @@ class HaRouter(router.RouterInfo): def get_router_cidrs(self, device): return set(self._ha_get_existing_cidrs(device.name)) - def _ha_external_gateway_removed(self, interface_name): - self._clear_vips(interface_name) - def routes_updated(self): new_routes = self.router['routes'] @@ -216,7 +213,7 @@ class HaRouter(router.RouterInfo): return False return True - def _ha_disable_addressing_on_interface(self, interface_name): + def _disable_ipv6_addressing_on_interface(self, interface_name): """Disable IPv6 link local addressing on the device and add it as a VIP to keepalived. This means that the IPv6 link local address will only be present on the master. @@ -230,15 +227,10 @@ class HaRouter(router.RouterInfo): self._remove_vip(ipv6_lladdr) self._add_vip(ipv6_lladdr, interface_name, scope='link') - def _ha_external_gateway_added(self, ex_gw_port, interface_name): + def _add_gateway_vip(self, ex_gw_port, interface_name): self._add_vip(ex_gw_port['ip_cidr'], interface_name) self._add_default_gw_virtual_route(ex_gw_port, interface_name) - def _ha_external_gateway_updated(self, ex_gw_port, interface_name): - old_gateway_cidr = self.ex_gw_port['ip_cidr'] - self._remove_vip(old_gateway_cidr) - self._ha_external_gateway_added(ex_gw_port, interface_name) - def add_floating_ip(self, fip, interface_name, device): fip_ip = fip['floating_ip_address'] ip_cidr = common_utils.ip_to_cidr(fip_ip) @@ -261,7 +253,7 @@ class HaRouter(router.RouterInfo): namespace=self.ns_name, prefix=router.INTERNAL_DEV_PREFIX) - self._ha_disable_addressing_on_interface(interface_name) + self._disable_ipv6_addressing_on_interface(interface_name) self._add_vip(port['ip_cidr'], interface_name) def internal_network_removed(self, port): @@ -319,3 +311,20 @@ class HaRouter(router.RouterInfo): state = 'master' if ha_cidr in cidrs else 'backup' self.ha_state = state callback(self.router_id, state) + + def external_gateway_added(self, ex_gw_port, interface_name): + self._plug_external_gateway(ex_gw_port, interface_name, self.ns_name) + self._add_gateway_vip(ex_gw_port, interface_name) + self._disable_ipv6_addressing_on_interface(interface_name) + + def external_gateway_updated(self, ex_gw_port, interface_name): + self._plug_external_gateway(ex_gw_port, interface_name, self.ns_name) + old_gateway_cidr = self.ex_gw_port['ip_cidr'] + self._remove_vip(old_gateway_cidr) + self._add_gateway_vip(ex_gw_port, interface_name) + + def external_gateway_removed(self, ex_gw_port, interface_name): + self._clear_vips(interface_name) + + super(HaRouter, self).external_gateway_removed(ex_gw_port, + interface_name) diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index 43cd6ef83..43ec8ae86 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -26,6 +26,7 @@ from neutron.i18n import _LE, _LW LOG = logging.getLogger(__name__) INTERNAL_DEV_PREFIX = namespaces.INTERNAL_DEV_PREFIX +EXTERNAL_DEV_PREFIX = namespaces.EXTERNAL_DEV_PREFIX class RouterInfo(object): @@ -88,6 +89,12 @@ class RouterInfo(object): 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_external_device_interface_name(self, ex_gw_port): + return self.get_external_device_name(ex_gw_port['id']) + def _set_subnet_info(self, port): ips = port['fixed_ips'] if not ips: @@ -101,8 +108,9 @@ class RouterInfo(object): def perform_snat_action(self, snat_callback, *args): # Process SNAT rules for attached subnets if self._snat_action: - snat_callback(self, self._router.get('gw_port'), - *args, action=self._snat_action) + snat_callback(self._router.get('gw_port'), + *args, + action=self._snat_action) self._snat_action = None def _update_routing_table(self, operation, route): @@ -338,8 +346,7 @@ class RouterInfo(object): existing_devices = self._get_existing_devices() current_internal_devs = set(n for n in existing_devices - if n.startswith( - INTERNAL_DEV_PREFIX)) + if n.startswith(INTERNAL_DEV_PREFIX)) current_port_devs = set(self.get_internal_device_name(port_id) for port_id in current_port_ids) stale_devs = current_internal_devs - current_port_devs @@ -349,3 +356,163 @@ class RouterInfo(object): self.driver.unplug(stale_dev, namespace=self.ns_name, prefix=INTERNAL_DEV_PREFIX) + + def _list_floating_ip_cidrs(self): + # Compute a list of addresses this router is supposed to have. + # This avoids unnecessarily removing those addresses and + # causing a momentarily network outage. + floating_ips = self.get_floating_ips() + return [common_utils.ip_to_cidr(ip['floating_ip_address']) + for ip in floating_ips] + + def _plug_external_gateway(self, ex_gw_port, interface_name, ns_name): + if not ip_lib.device_exists(interface_name, namespace=ns_name): + self.driver.plug(ex_gw_port['network_id'], + ex_gw_port['id'], + interface_name, + ex_gw_port['mac_address'], + bridge=self.agent_conf.external_network_bridge, + namespace=ns_name, + prefix=EXTERNAL_DEV_PREFIX) + + def _external_gateway_added(self, ex_gw_port, interface_name, + ns_name, preserve_ips): + self._plug_external_gateway(ex_gw_port, interface_name, ns_name) + + self.driver.init_l3(interface_name, + [ex_gw_port['ip_cidr']], + namespace=ns_name, + gateway=ex_gw_port['subnet'].get('gateway_ip'), + extra_subnets=ex_gw_port.get('extra_subnets', []), + preserve_ips=preserve_ips) + ip_address = ex_gw_port['ip_cidr'].split('/')[0] + ip_lib.send_gratuitous_arp(ns_name, + interface_name, + ip_address, + self.agent_conf.send_arp_for_ha) + + def external_gateway_added(self, ex_gw_port, interface_name): + preserve_ips = self._list_floating_ip_cidrs() + self._external_gateway_added( + ex_gw_port, interface_name, self.ns_name, preserve_ips) + + def external_gateway_updated(self, ex_gw_port, interface_name): + preserve_ips = self._list_floating_ip_cidrs() + self._external_gateway_added( + ex_gw_port, interface_name, self.ns_name, preserve_ips) + + def external_gateway_removed(self, ex_gw_port, interface_name): + self.driver.unplug(interface_name, + bridge=self.agent_conf.external_network_bridge, + namespace=self.ns_name, + prefix=EXTERNAL_DEV_PREFIX) + + def _process_external_gateway(self, ex_gw_port): + # TODO(Carl) Refactor to clarify roles of ex_gw_port vs self.ex_gw_port + ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or + self.ex_gw_port and self.ex_gw_port['id']) + + interface_name = None + if ex_gw_port_id: + interface_name = self.get_external_device_name(ex_gw_port_id) + if ex_gw_port: + def _gateway_ports_equal(port1, port2): + def _get_filtered_dict(d, ignore): + return dict((k, v) for k, v in d.iteritems() + if k not in ignore) + + keys_to_ignore = set(['binding:host_id']) + port1_filtered = _get_filtered_dict(port1, keys_to_ignore) + port2_filtered = _get_filtered_dict(port2, keys_to_ignore) + return port1_filtered == port2_filtered + + self._set_subnet_info(ex_gw_port) + if not self.ex_gw_port: + self.external_gateway_added(ex_gw_port, interface_name) + elif not _gateway_ports_equal(ex_gw_port, self.ex_gw_port): + self.external_gateway_updated(ex_gw_port, interface_name) + elif not ex_gw_port and self.ex_gw_port: + self.external_gateway_removed(self.ex_gw_port, interface_name) + + existing_devices = self._get_existing_devices() + stale_devs = [dev for dev in existing_devices + if dev.startswith(EXTERNAL_DEV_PREFIX) + and dev != interface_name] + for stale_dev in stale_devs: + LOG.debug('Deleting stale external router device: %s', stale_dev) + self.driver.unplug(stale_dev, + bridge=self.agent_conf.external_network_bridge, + namespace=self.ns_name, + prefix=EXTERNAL_DEV_PREFIX) + + # Process SNAT rules for external gateway + self.perform_snat_action(self._handle_router_snat_rules, + interface_name) + + def external_gateway_nat_rules(self, ex_gw_ip, interface_name): + rules = [('POSTROUTING', '! -i %(interface_name)s ' + '! -o %(interface_name)s -m conntrack ! ' + '--ctstate DNAT -j ACCEPT' % + {'interface_name': interface_name}), + ('snat', '-o %s -j SNAT --to-source %s' % + (interface_name, ex_gw_ip))] + return rules + + def _empty_snat_chains(self, iptables_manager): + iptables_manager.ipv4['nat'].empty_chain('POSTROUTING') + iptables_manager.ipv4['nat'].empty_chain('snat') + + def _add_snat_rules(self, ex_gw_port, iptables_manager, + interface_name, action): + if action == 'add_rules' and ex_gw_port: + # ex_gw_port should not be None in this case + # NAT rules are added only if ex_gw_port has an IPv4 address + for ip_addr in ex_gw_port['fixed_ips']: + ex_gw_ip = ip_addr['ip_address'] + if netaddr.IPAddress(ex_gw_ip).version == 4: + rules = self.external_gateway_nat_rules(ex_gw_ip, + interface_name) + for rule in rules: + iptables_manager.ipv4['nat'].add_rule(*rule) + break + + def _handle_router_snat_rules(self, ex_gw_port, + interface_name, action): + self._empty_snat_chains(self.iptables_manager) + + self.iptables_manager.ipv4['nat'].add_rule('snat', '-j $float-snat') + + self._add_snat_rules(ex_gw_port, + self.iptables_manager, + interface_name, + action) + + def process_external(self, agent): + existing_floating_ips = self.floating_ips + try: + with self.iptables_manager.defer_apply(): + ex_gw_port = self.get_ex_gw_port() + self._process_external_gateway(ex_gw_port) + # TODO(Carl) Return after setting existing_floating_ips and + # still call update_fip_statuses? + if not ex_gw_port: + return + + # Process SNAT/DNAT rules and addresses for floating IPs + if self.router['distributed']: + self.create_dvr_fip_interfaces(ex_gw_port) + self.process_snat_dnat_for_fip() + + # Once NAT rules for floating IPs are safely in place + # configure their addresses on the external gateway port + interface_name = self.get_external_device_interface_name( + ex_gw_port) + fip_statuses = self.configure_fip_addresses(interface_name) + + except (n_exc.FloatingIpSetupException, + n_exc.IpTablesApplyException) as e: + # All floating IPs must be put in error state + LOG.exception(e) + fip_statuses = self.put_fips_in_error_state() + + agent.update_fip_statuses(self, existing_floating_ips, fip_statuses) diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index 01eef6469..b0147bd9b 100755 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -157,7 +157,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase): external_port = router.get_ex_gw_port() ex_port_ipv6 = router._get_ipv6_lladdr( external_port['mac_address']) - external_device_name = self.agent.get_external_device_name( + external_device_name = router.get_external_device_name( external_port['id']) external_device_cidr = external_port['ip_cidr'] internal_port = router.router[l3_constants.INTERFACE_KEY][0] @@ -270,7 +270,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase): floating_ips = router.router[l3_constants.FLOATINGIP_KEY] external_port = router.get_ex_gw_port() return len(floating_ips) and all(ip_lib.device_exists_with_ip_mac( - self.agent.get_external_device_name(external_port['id']), + router.get_external_device_name(external_port['id']), '%s/32' % fip['floating_ip_address'], external_port['mac_address'], namespace=router.ns_name) for fip in floating_ips) @@ -433,7 +433,7 @@ class L3AgentTestCase(L3AgentTestFramework): self.assertNotIn(old_gw, new_config) self.assertIn(new_gw, new_config) external_port = router.get_ex_gw_port() - external_device_name = self.agent.get_external_device_name( + external_device_name = router.get_external_device_name( external_port['id']) self.assertNotIn('%s/24 dev %s' % (old_external_device_ip, external_device_name), @@ -448,7 +448,7 @@ class L3AgentTestCase(L3AgentTestFramework): if enable_ha: port = router.get_ex_gw_port() - interface_name = self.agent.get_external_device_name(port['id']) + interface_name = router.get_external_device_name(port['id']) self._assert_no_ip_addresses_on_interface(router.ns_name, interface_name) utils.wait_until_true(lambda: router.ha_state == 'master') @@ -496,12 +496,12 @@ class L3AgentTestCase(L3AgentTestFramework): def _assert_external_device(self, router): external_port = router.get_ex_gw_port() self.assertTrue(self.device_exists_with_ip_mac( - external_port, self.agent.get_external_device_name, + external_port, router.get_external_device_name, router.ns_name)) def _assert_gateway(self, router): external_port = router.get_ex_gw_port() - external_device_name = self.agent.get_external_device_name( + external_device_name = router.get_external_device_name( external_port['id']) external_device = ip_lib.IPDevice(external_device_name, namespace=router.ns_name) @@ -797,7 +797,7 @@ class TestDvrRouter(L3AgentTestFramework): # snat_ns_name namespace if self.agent.conf.agent_mode == 'dvr_snat': self.assertTrue(self.device_exists_with_ip_mac( - external_port, self.agent.get_external_device_name, + external_port, router.get_external_device_name, snat_ns_name)) # if the agent is in dvr mode then the snat_ns_name namespace # should not be present at all: @@ -829,7 +829,7 @@ class TestDvrRouter(L3AgentTestFramework): namespace = dvr_snat_ns.SnatNamespace.get_snat_ns_name( router.router_id) external_port = router.get_ex_gw_port() - external_device_name = self.agent.get_external_device_name( + external_device_name = router.get_external_device_name( external_port['id']) external_device = ip_lib.IPDevice(external_device_name, namespace=namespace) diff --git a/neutron/tests/unit/test_l3_agent.py b/neutron/tests/unit/test_l3_agent.py index a88be8183..a34ee482a 100644 --- a/neutron/tests/unit/test_l3_agent.py +++ b/neutron/tests/unit/test_l3_agent.py @@ -412,6 +412,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri._set_subnet_info = mock.Mock() ri._set_subnet_arp_info = mock.Mock() ri._internal_network_added = mock.Mock() + ri._set_subnet_arp_info = mock.Mock() ri.internal_network_added(port) self.assertEqual(ri._snat_redirect_add.call_count, 1) self.assertEqual(ri._set_subnet_info.call_count, 1) @@ -455,12 +456,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): if router.get('distributed'): agent.conf.agent_mode = 'dvr_snat' agent.host = HOSTNAME - agent._create_dvr_gateway = mock.Mock() ri = dvr_router.DvrRouter(agent, HOSTNAME, router['id'], router, **self.ri_kwargs) + ri._create_dvr_gateway = mock.Mock() ri.get_snat_interfaces = mock.Mock(return_value=self.snat_ports) ri.create_snat_namespace() ri.fip_ns = agent.get_fip_ns(ex_net_id) @@ -478,7 +479,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'network_id': ex_net_id, 'mac_address': 'ca:fe:de:ad:be:ef', 'ip_cidr': '20.0.0.30/24'} - interface_name = agent.get_external_device_name(ex_gw_port['id']) + interface_name = ri.get_external_device_name(ex_gw_port['id']) if action == 'add': self.device_exists.return_value = False @@ -487,7 +488,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'fixed_ip_address': '192.168.0.1', 'port_id': _uuid()}]} router[l3_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] - agent.external_gateway_added(ri, ex_gw_port, interface_name) + ri.external_gateway_added(ex_gw_port, interface_name) if not router.get('distributed'): self.assertEqual(self.mock_driver.plug.call_count, 1) self.assertEqual(self.mock_driver.init_l3.call_count, 1) @@ -502,15 +503,15 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ['20.0.0.30/24'], **kwargs) else: - agent._create_dvr_gateway.assert_called_once_with( - ri, ex_gw_port, interface_name, + ri._create_dvr_gateway.assert_called_once_with( + ex_gw_port, interface_name, self.snat_ports) elif action == 'remove': self.device_exists.return_value = True - agent._map_internal_interfaces = mock.Mock(return_value=sn_port) + ri._map_internal_interfaces = mock.Mock(return_value=sn_port) ri._snat_redirect_remove = mock.Mock() - agent.external_gateway_removed(ri, ex_gw_port, interface_name) + ri.external_gateway_removed(ex_gw_port, interface_name) if not router.get('distributed'): self.mock_driver.unplug.assert_called_once_with( interface_name, @@ -525,7 +526,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): else: raise Exception("Invalid action %s" % action) - def _prepare_ext_gw_test(self, agent): + def _prepare_ext_gw_test(self, ri): ex_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', 'subnet_id': _uuid()}], 'subnet': {'gateway_ip': '20.0.0.1'}, @@ -534,7 +535,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'network_id': _uuid(), 'mac_address': 'ca:fe:de:ad:be:ef', 'ip_cidr': '20.0.0.30/24'} - interface_name = agent.get_external_device_name(ex_gw_port['id']) + interface_name = ri.get_external_device_name(ex_gw_port['id']) self.device_exists.return_value = True @@ -542,17 +543,15 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_external_gateway_updated(self): router = prepare_router_data(num_internal_ports=2) - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - interface_name, ex_gw_port = self._prepare_ext_gw_test(agent) + interface_name, ex_gw_port = self._prepare_ext_gw_test(ri) fake_fip = {'floatingips': [{'id': _uuid(), 'floating_ip_address': '192.168.1.34', 'fixed_ip_address': '192.168.0.1', 'port_id': _uuid()}]} router[l3_constants.FLOATINGIP_KEY] = fake_fip['floatingips'] - agent.external_gateway_updated(ri, ex_gw_port, - interface_name) + ri.external_gateway_updated(ex_gw_port, interface_name) self.assertEqual(self.mock_driver.plug.call_count, 0) self.assertEqual(self.mock_driver.init_l3.call_count, 1) self.send_arp.assert_called_once_with(ri.ns_name, interface_name, @@ -575,19 +574,17 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router, **self.ri_kwargs) ri.create_snat_namespace() - interface_name, ex_gw_port = self._prepare_ext_gw_test(agent) - agent._external_gateway_added = mock.Mock() + interface_name, ex_gw_port = self._prepare_ext_gw_test(ri) + ri._external_gateway_added = mock.Mock() # test agent mode = dvr (compute node) - router['distributed'] = True router['gw_port_host'] = host agent.conf.agent_mode = agent_mode - agent.external_gateway_updated(ri, ex_gw_port, - interface_name) + ri.external_gateway_updated(ex_gw_port, interface_name) # no gateway should be added on dvr node self.assertEqual(expected_call_count, - agent._external_gateway_added.call_count) + ri._external_gateway_added.call_count) def test_ext_gw_updated_dvr_agent_mode(self): # no gateway should be added on dvr node @@ -599,7 +596,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_ext_gw_updated_dvr_snat_agent_mode_host(self): # gateway should be added on dvr_snat node - self._test_ext_gw_updated_dvr_agent_mode(self.conf.host, + self._test_ext_gw_updated_dvr_agent_mode(HOSTNAME, 'dvr_snat', 1) def test_agent_add_external_gateway(self): @@ -772,13 +769,13 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router = ri.router agent.host = HOSTNAME fake_fip_id = 'fake_fip_id' - agent.create_dvr_fip_interfaces = mock.Mock() + ri.create_dvr_fip_interfaces = mock.Mock() ri.process_floating_ip_addresses = mock.Mock() ri.process_floating_ip_nat_rules = mock.Mock() ri.process_floating_ip_addresses.return_value = { fake_fip_id: 'ACTIVE'} - agent.external_gateway_added = mock.Mock() - agent.external_gateway_updated = mock.Mock() + ri.external_gateway_added = mock.Mock() + ri.external_gateway_updated = mock.Mock() fake_floatingips1 = {'floatingips': [ {'id': fake_fip_id, 'floating_ip_address': '8.8.8.8', @@ -790,7 +787,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri.process_floating_ip_addresses.reset_mock() ri.process_floating_ip_nat_rules.assert_called_with() ri.process_floating_ip_nat_rules.reset_mock() - agent.external_gateway_added.reset_mock() + ri.external_gateway_added.reset_mock() # remap floating IP to a new fixed ip fake_floatingips2 = copy.deepcopy(fake_floatingips1) @@ -802,10 +799,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri.process_floating_ip_addresses.reset_mock() ri.process_floating_ip_nat_rules.assert_called_with() ri.process_floating_ip_nat_rules.reset_mock() - self.assertEqual(agent.external_gateway_added.call_count, 0) - self.assertEqual(agent.external_gateway_updated.call_count, 0) - agent.external_gateway_added.reset_mock() - agent.external_gateway_updated.reset_mock() + self.assertEqual(ri.external_gateway_added.call_count, 0) + self.assertEqual(ri.external_gateway_updated.call_count, 0) + ri.external_gateway_added.reset_mock() + ri.external_gateway_updated.reset_mock() # change the ex_gw_port a bit to test gateway update new_gw_port = copy.deepcopy(ri.router['gw_port']) @@ -817,8 +814,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent.process_router(ri) ri.process_floating_ip_addresses.reset_mock() ri.process_floating_ip_nat_rules.reset_mock() - self.assertEqual(agent.external_gateway_added.call_count, 0) - self.assertEqual(agent.external_gateway_updated.call_count, 1) + self.assertEqual(ri.external_gateway_added.call_count, 0) + self.assertEqual(ri.external_gateway_updated.call_count, 1) # remove just the floating ips del router[l3_constants.FLOATINGIP_KEY] @@ -853,7 +850,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): with mock.patch.object(lla.LinkLocalAllocator, '_write'): if ri.router['distributed']: ri.fip_ns = agent.get_fip_ns(ex_gw_port['network_id']) - agent.create_dvr_fip_interfaces(ri, ex_gw_port) + ri.create_dvr_fip_interfaces(ex_gw_port) fip_statuses = ri.process_floating_ip_addresses( mock.sentinel.interface_name) self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ACTIVE}, @@ -929,7 +926,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): sub_info): fips.return_value = fake_floatingips fip_gw_port.return_value = agent_gateway_port[0] - agent.create_dvr_fip_interfaces(ri, ext_gw_port) + ri.create_dvr_fip_interfaces(ext_gw_port) self.assertTrue(fip_gw_port.called) self.assertTrue(fips.called) self.assertEqual(ri.fip_ns.agent_gateway_port, @@ -949,7 +946,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.iptables_manager.ipv4['nat'] = mock.MagicMock() - agent.get_external_device_name = mock.Mock(return_value='exgw') + ri.get_external_device_name = mock.Mock(return_value='exgw') self._test_process_floating_ip_addresses_add(ri, agent) def test_process_router_dist_floating_ip_add(self): @@ -993,7 +990,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data(enable_snat=True) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process with NAT agent.process_router(ri) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] @@ -1014,7 +1011,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data(enable_snat=False) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process without NAT agent.process_router(ri) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] @@ -1035,7 +1032,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process with NAT agent.process_router(ri) # Add an interface and reprocess @@ -1053,7 +1050,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): gw_port = router['gw_port'] router['gw_port'] = None ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] @@ -1061,7 +1058,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router['gw_port'] = gw_port ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) with mock.patch.object( - agent, + ri, 'external_gateway_nat_rules') as external_gateway_nat_rules: self._process_router_instance_for_agent(agent, ri, router) new_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] @@ -1074,7 +1071,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self, router, ra_mode=None, addr_mode=None): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process with NAT agent.process_router(ri) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] @@ -1132,7 +1129,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process with NAT agent.process_router(ri) # Add an IPv4 and IPv6 interface and reprocess @@ -1146,7 +1143,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data(num_internal_ports=2) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # Process with NAT agent.process_router(ri) # Add an interface and reprocess @@ -1161,7 +1158,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface and reprocess router_append_interface(router, count=1, ip_version=6) @@ -1179,7 +1176,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() with mock.patch.object( ri, 'internal_network_added') as internal_network_added: @@ -1204,7 +1201,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) router = prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() # add an internal port agent.process_router(ri) @@ -1246,7 +1243,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri = legacy_router.LegacyRouter(router['id'], router, **self.ri_kwargs) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() agent.process_router(ri) # Assess the call for putting the floating IP up was performed mock_update_fip_status.assert_called_once_with( @@ -1278,7 +1275,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.process_floating_ip_addresses = mock.Mock( side_effect=RuntimeError) - agent.external_gateway_added = mock.Mock() + ri.external_gateway_added = mock.Mock() agent.process_router(ri) # Assess the call for putting the floating IP into Error # was performed @@ -1301,7 +1298,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.iptables_manager._apply = mock.Mock(side_effect=Exception) - agent._process_external(ri) + ri.process_external(agent) # Assess the call for putting the floating IP into Error # was performed mock_update_fip_status.assert_called_once_with( @@ -1316,24 +1313,22 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent, HOSTNAME, 'foo_router_id', - {'distributed': True}, + {}, **self.ri_kwargs) ri.iptables_manager = mock.Mock() - with mock.patch.object(l3_agent.LOG, 'debug') as log_debug: - agent._handle_router_snat_rules( - ri, mock.ANY, mock.ANY, mock.ANY) + with mock.patch.object(dvr_router.LOG, 'debug') as log_debug: + ri._handle_router_snat_rules(mock.ANY, mock.ANY, mock.ANY) self.assertIsNone(ri.snat_iptables_manager) self.assertFalse(ri.iptables_manager.called) self.assertTrue(log_debug.called) def test_handle_router_snat_rules_add_back_jump(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = mock.MagicMock() + ri = l3router.RouterInfo(_uuid(), {}, **self.ri_kwargs) + ri.iptables_manager = mock.MagicMock() port = {'fixed_ips': [{'ip_address': '192.168.1.4'}]} - ri.router = {'distributed': False} - agent._handle_router_snat_rules(ri, port, "iface", "add_rules") + ri._handle_router_snat_rules(port, "iface", "add_rules") nat = ri.iptables_manager.ipv4['nat'] nat.empty_chain.assert_any_call('snat') @@ -1346,12 +1341,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): break def test_handle_router_snat_rules_add_rules(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(_uuid(), {}, **self.ri_kwargs) ex_gw_port = {'fixed_ips': [{'ip_address': '192.168.1.4'}]} ri.router = {'distributed': False} - agent._handle_router_snat_rules(ri, ex_gw_port, - "iface", "add_rules") + ri._handle_router_snat_rules(ex_gw_port, "iface", "add_rules") nat_rules = map(str, ri.iptables_manager.ipv4['nat'].rules) wrap_name = ri.iptables_manager.wrap_name @@ -1388,9 +1381,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'internal_network_removed'), mock.patch.object(ri, 'internal_network_added'), - mock.patch.object(l3_agent.L3NATAgent, + mock.patch.object(ri, 'external_gateway_removed'), - mock.patch.object(l3_agent.L3NATAgent, + mock.patch.object(ri, 'external_gateway_added') ) as (internal_network_removed, internal_network_added, @@ -1769,8 +1762,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): interface_name = ri.get_snat_int_device_name(port_id) self.device_exists.return_value = False - agent._create_dvr_gateway(ri, dvr_gw_port, interface_name, - self.snat_ports) + ri._create_dvr_gateway(dvr_gw_port, interface_name, self.snat_ports) # check 2 internal ports are plugged # check 1 ext-gw-port is plugged @@ -1818,7 +1810,6 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' router = prepare_router_data(num_internal_ports=2) - router['distributed'] = True router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() @@ -1858,9 +1849,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.device_exists.return_value = True fip_ns = ri.fip_ns - agent.external_gateway_removed( - ri, ri.ex_gw_port, - agent.get_external_device_name(ri.ex_gw_port['id'])) + ri.snat_namespace = mock.Mock() + ri.external_gateway_removed( + ri.ex_gw_port, + ri.get_external_device_name(ri.ex_gw_port['id'])) if fip_ns: ri.remove_floating_ip.assert_called_once_with(self.mock_ip_dev, -- 2.45.2