From 35654ec23ef9db6bda313ea300ab76c287a98ceb Mon Sep 17 00:00:00 2001 From: Gal Sagie Date: Mon, 25 May 2015 15:20:05 +0300 Subject: [PATCH] Decompose DVR CSNAT L3 Agent from Compute Node L3 Agent Currently the same dvr router class is used both by the L3 Agent in the compute nodes that is responsible for the virtual routers namespace and the fip namespace and also used by the centralized SNAT L3 Agent in the network node. This is the first step to decompose the two into different classes. The above means that we have one class of DVR router which is used for two jobs (the virtual router namespace wiring and the fips wiring in the compute node in one hand and the centralized snat wiring in the other) The end goal of this patch is to separate the two into different classes which will also help maintaining it and also help projects that want to use one but not the other (for example only use the centralized SNAT behaviour with there own DVR implementation) Change-Id: I581a097b9e7c49f20d0eb0e4ca66a25e90d9511b Partial-Bug: #1458541 Partially-Implements: blueprint dvr-router-code-decompose --- neutron/agent/l3/agent.py | 8 +- neutron/agent/l3/dvr_edge_router.py | 148 ++++ .../l3/{dvr_router.py => dvr_local_router.py} | 117 +-- neutron/tests/common/l3_test_common.py | 268 +++++++ .../tests/functional/agent/test_l3_agent.py | 15 +- neutron/tests/unit/agent/l3/test_agent.py | 683 ++++-------------- .../unit/agent/l3/test_dvr_local_router.py | 573 +++++++++++++++ .../tests/unit/agent/l3/test_dvr_router.py | 214 ------ 8 files changed, 1145 insertions(+), 881 deletions(-) create mode 100644 neutron/agent/l3/dvr_edge_router.py rename neutron/agent/l3/{dvr_router.py => dvr_local_router.py} (79%) create mode 100644 neutron/tests/common/l3_test_common.py create mode 100644 neutron/tests/unit/agent/l3/test_dvr_local_router.py delete mode 100644 neutron/tests/unit/agent/l3/test_dvr_router.py diff --git a/neutron/agent/l3/agent.py b/neutron/agent/l3/agent.py index 6b25b9310..f91c7220a 100644 --- a/neutron/agent/l3/agent.py +++ b/neutron/agent/l3/agent.py @@ -23,7 +23,8 @@ from oslo_utils import importutils from oslo_utils import timeutils from neutron.agent.l3 import dvr -from neutron.agent.l3 import dvr_router +from neutron.agent.l3 import dvr_edge_router as dvr_router +from neutron.agent.l3 import dvr_local_router as dvr_local_router from neutron.agent.l3 import ha from neutron.agent.l3 import ha_router from neutron.agent.l3 import legacy_router @@ -300,7 +301,10 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback, if router.get('distributed'): kwargs['agent'] = self kwargs['host'] = self.host - return dvr_router.DvrRouter(*args, **kwargs) + if self.conf.agent_mode == l3_constants.L3_AGENT_MODE_DVR_SNAT: + return dvr_router.DvrEdgeRouter(*args, **kwargs) + else: + return dvr_local_router.DvrLocalRouter(*args, **kwargs) if router.get('ha'): kwargs['state_change_callback'] = self.enqueue_state_change diff --git a/neutron/agent/l3/dvr_edge_router.py b/neutron/agent/l3/dvr_edge_router.py new file mode 100644 index 000000000..167df080a --- /dev/null +++ b/neutron/agent/l3/dvr_edge_router.py @@ -0,0 +1,148 @@ +# Copyright (c) 2015 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. + +from oslo_log import log as logging + +from neutron.agent.l3 import dvr_local_router +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 + +LOG = logging.getLogger(__name__) + + +class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): + + def __init__(self, agent, host, *args, **kwargs): + super(DvrEdgeRouter, self).__init__(agent, host, *args, **kwargs) + self.snat_namespace = None + + def external_gateway_added(self, ex_gw_port, interface_name): + super(DvrEdgeRouter, self).external_gateway_added( + ex_gw_port, interface_name) + if self._is_this_snat_host(): + snat_ports = self.get_snat_interfaces() + self._create_dvr_gateway(ex_gw_port, interface_name, snat_ports) + + 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): + super(DvrEdgeRouter, self).external_gateway_removed(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.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 internal_network_added(self, port): + super(DvrEdgeRouter, self).internal_network_added(port) + + # TODO(gsagie) some of this checks are already implemented + # in the base class, think how to avoid re-doing them + if not self._is_this_snat_host(): + return + + snat_ports = self.get_snat_interfaces() + sn_port = self._map_internal_interfaces(port, snat_ports) + if not sn_port: + return + + ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id']) + interface_name = self.get_snat_int_device_name(sn_port['id']) + self._internal_network_added( + ns_name, + sn_port['network_id'], + sn_port['id'], + sn_port['fixed_ips'], + sn_port['mac_address'], + interface_name, + dvr_snat_ns.SNAT_INT_DEV_PREFIX) + + def _dvr_internal_network_removed(self, port): + super(DvrEdgeRouter, self)._dvr_internal_network_removed(port) + + if not self.ex_gw_port: + return + + sn_port = self._map_internal_interfaces(port, self.snat_ports) + if not sn_port: + return + + is_this_snat_host = ('binding:host_id' in self.ex_gw_port) and ( + self.ex_gw_port['binding:host_id'] == self.host) + if not is_this_snat_host: + return + + snat_interface = self.get_snat_int_device_name(sn_port['id']) + ns_name = self.snat_namespace.name + prefix = dvr_snat_ns.SNAT_INT_DEV_PREFIX + if ip_lib.device_exists(snat_interface, namespace=ns_name): + self.driver.unplug(snat_interface, namespace=ns_name, + prefix=prefix) + + 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 + interface_name = self.get_snat_int_device_name(port['id']) + self._internal_network_added( + snat_ns.name, port['network_id'], + port['id'], port['fixed_ips'], + 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 create_snat_namespace(self): + # TODO(mlavalle): in the near future, this method should contain the + # code in the L3 agent that creates a gateway for a dvr. The first step + # is to move the creation of the snat namespace here + self.snat_namespace = dvr_snat_ns.SnatNamespace(self.router['id'], + self.agent_conf, + self.driver, + self.use_ipv6) + self.snat_namespace.create() + return self.snat_namespace + + def get_snat_int_device_name(self, port_id): + long_name = dvr_snat_ns.SNAT_INT_DEV_PREFIX + port_id + return long_name[:self.driver.DEV_NAME_LEN] + + def _is_this_snat_host(self): + return self.get_gw_port_host() == self.host diff --git a/neutron/agent/l3/dvr_router.py b/neutron/agent/l3/dvr_local_router.py similarity index 79% rename from neutron/agent/l3/dvr_router.py rename to neutron/agent/l3/dvr_local_router.py index df3d465e4..01f034872 100755 --- a/neutron/agent/l3/dvr_router.py +++ b/neutron/agent/l3/dvr_local_router.py @@ -19,10 +19,8 @@ from oslo_log import log as logging from oslo_utils import excutils 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 exceptions from neutron.common import utils as common_utils @@ -33,9 +31,9 @@ LOG = logging.getLogger(__name__) MASK_30 = 0x3fffffff -class DvrRouter(router.RouterInfo): +class DvrLocalRouter(router.RouterInfo): def __init__(self, agent, host, *args, **kwargs): - super(DvrRouter, self).__init__(*args, **kwargs) + super(DvrLocalRouter, self).__init__(*args, **kwargs) self.agent = agent self.host = host @@ -45,21 +43,16 @@ class DvrRouter(router.RouterInfo): # Linklocal subnet for router and floating IP namespace link self.rtr_fip_subnet = None self.dist_fip_count = None - self.snat_namespace = None self.fip_ns = None def get_floating_ips(self): """Filter Floating IPs to be hosted on this agent.""" - floating_ips = super(DvrRouter, self).get_floating_ips() + floating_ips = super(DvrLocalRouter, self).get_floating_ips() return [i for i in floating_ips if i['host'] == self.host] def get_snat_interfaces(self): return self.router.get(l3_constants.SNAT_ROUTER_INTF_KEY, []) - def get_snat_int_device_name(self, port_id): - long_name = dvr_snat_ns.SNAT_INT_DEV_PREFIX + port_id - return long_name[:self.driver.DEV_NAME_LEN] - def _handle_fip_nat_rules(self, interface_name, action): """Configures NAT rules for Floating IPs for DVR. @@ -159,20 +152,9 @@ class DvrRouter(router.RouterInfo): return l3_constants.FLOATINGIP_STATUS_ACTIVE def remove_floating_ip(self, device, ip_cidr): - super(DvrRouter, self).remove_floating_ip(device, ip_cidr) + super(DvrLocalRouter, self).remove_floating_ip(device, ip_cidr) self.floating_ip_removed_dist(ip_cidr) - def create_snat_namespace(self): - # TODO(mlavalle): in the near future, this method should contain the - # code in the L3 agent that creates a gateway for a dvr. The first step - # is to move the creation of the snat namespace here - self.snat_namespace = dvr_snat_ns.SnatNamespace(self.router['id'], - self.agent_conf, - self.driver, - self.use_ipv6) - self.snat_namespace.create() - return self.snat_namespace - def _get_internal_port(self, subnet_id): """Return internal router port based on subnet_id.""" router_ports = self.router.get(l3_constants.INTERFACE_KEY, []) @@ -309,14 +291,8 @@ 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) + super(DvrLocalRouter, self).internal_network_added(port) # NOTE: The following function _set_subnet_arp_info # should be called to dynamically populate the arp @@ -338,20 +314,6 @@ class DvrRouter(router.RouterInfo): interface_name = self.get_internal_device_name(port['id']) self._snat_redirect_add(sn_port, port, interface_name) - if not self._is_this_snat_host(): - return - - ns_name = dvr_snat_ns.SnatNamespace.get_snat_ns_name(self.router['id']) - interface_name = self.get_snat_int_device_name(sn_port['id']) - self._internal_network_added( - ns_name, - sn_port['network_id'], - sn_port['id'], - sn_port['fixed_ips'], - sn_port['mac_address'], - interface_name, - dvr_snat_ns.SNAT_INT_DEV_PREFIX) - def _dvr_internal_network_removed(self, port): if not self.ex_gw_port: return @@ -364,23 +326,9 @@ class DvrRouter(router.RouterInfo): interface_name = self.get_internal_device_name(port['id']) self._snat_redirect_remove(sn_port, port, interface_name) - 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 - - snat_interface = ( - self.get_snat_int_device_name(sn_port['id'])) - ns_name = self.snat_namespace.name - prefix = dvr_snat_ns.SNAT_INT_DEV_PREFIX - if ip_lib.device_exists(snat_interface, namespace=ns_name): - self.driver.unplug(snat_interface, namespace=ns_name, - prefix=prefix) - def internal_network_removed(self, port): self._dvr_internal_network_removed(port) - super(DvrRouter, self).internal_network_removed(port) + super(DvrLocalRouter, self).internal_network_removed(port) def get_floating_agent_gw_interface(self, ext_net_id): """Filter Floating Agent GW port for the external network.""" @@ -393,26 +341,6 @@ class DvrRouter(router.RouterInfo): 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 - interface_name = self.get_snat_int_device_name(port['id']) - self._internal_network_added(snat_ns.name, port['network_id'], - port['id'], port['fixed_ips'], - 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, @@ -427,9 +355,6 @@ class DvrRouter(router.RouterInfo): if gateway: self._snat_redirect_add(gateway, 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'], @@ -438,15 +363,7 @@ class DvrRouter(router.RouterInfo): '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=[]) + pass def external_gateway_removed(self, ex_gw_port, interface_name): # TODO(Carl) Should this be calling process_snat_dnat_for_fip? @@ -461,19 +378,6 @@ class DvrRouter(router.RouterInfo): internal_interface = self.get_internal_device_name(p['id']) self._snat_redirect_remove(gateway, 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: @@ -495,13 +399,14 @@ class DvrRouter(router.RouterInfo): if self.get_gw_port_host() != self.host: return - super(DvrRouter, self).perform_snat_action(snat_callback, *args) + super(DvrLocalRouter, + self).perform_snat_action(snat_callback, *args) def process_external(self, agent): ex_gw_port = self.get_ex_gw_port() if ex_gw_port: self.create_dvr_fip_interfaces(ex_gw_port) - super(DvrRouter, self).process_external(agent) + super(DvrLocalRouter, self).process_external(agent) def create_dvr_fip_interfaces(self, ex_gw_port): floating_ips = self.get_floating_ips() @@ -532,4 +437,4 @@ class DvrRouter(router.RouterInfo): self.fip_ns = agent.get_fip_ns(ex_gw_port['network_id']) self.fip_ns.scan_fip_ports(self) - super(DvrRouter, self).process(agent) + super(DvrLocalRouter, self).process(agent) diff --git a/neutron/tests/common/l3_test_common.py b/neutron/tests/common/l3_test_common.py new file mode 100644 index 000000000..7345d95cf --- /dev/null +++ b/neutron/tests/common/l3_test_common.py @@ -0,0 +1,268 @@ +# Copyright (c) 2015 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 copy +import netaddr +from six import moves + +from neutron.common import constants as l3_constants +from neutron.openstack.common import uuidutils + +_uuid = uuidutils.generate_uuid + + +class FakeDev(object): + def __init__(self, name): + self.name = name + + +def get_ha_interface(ip='169.254.192.1', mac='12:34:56:78:2b:5d'): + subnet_id = _uuid() + return {'admin_state_up': True, + 'device_id': _uuid(), + 'device_owner': 'network:router_ha_interface', + 'fixed_ips': [{'ip_address': ip, + 'prefixlen': 18, + 'subnet_id': subnet_id}], + 'id': _uuid(), + 'mac_address': mac, + 'name': u'L3 HA Admin port 0', + 'network_id': _uuid(), + 'status': u'ACTIVE', + 'subnets': [{'cidr': '169.254.192.0/18', + 'gateway_ip': '169.254.255.254', + 'id': subnet_id}], + 'tenant_id': '', + 'agent_id': _uuid(), + 'agent_host': 'aaa', + 'priority': 1} + + +def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1, + enable_floating_ip=False, enable_ha=False, + extra_routes=False, dual_stack=False, + v6_ext_gw_with_sub=True, **kwargs): + fixed_ips = [] + subnets = [] + gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee') + for loop_version in (4, 6): + if loop_version == 4 and (ip_version == 4 or dual_stack): + ip_address = kwargs.get('ip_address', '19.4.4.4') + prefixlen = 24 + subnet_cidr = kwargs.get('subnet_cidr', '19.4.4.0/24') + gateway_ip = kwargs.get('gateway_ip', '19.4.4.1') + elif (loop_version == 6 and (ip_version == 6 or dual_stack) and + v6_ext_gw_with_sub): + ip_address = kwargs.get('ip_address', 'fd00::4') + prefixlen = 64 + subnet_cidr = kwargs.get('subnet_cidr', 'fd00::/64') + gateway_ip = kwargs.get('gateway_ip', 'fd00::1') + else: + continue + subnet_id = _uuid() + fixed_ips.append({'ip_address': ip_address, + 'subnet_id': subnet_id, + 'prefixlen': prefixlen}) + subnets.append({'id': subnet_id, + 'cidr': subnet_cidr, + 'gateway_ip': gateway_ip}) + if not fixed_ips and v6_ext_gw_with_sub: + raise ValueError("Invalid ip_version: %s" % ip_version) + + router_id = _uuid() + ex_gw_port = {'id': _uuid(), + 'mac_address': gateway_mac, + 'network_id': _uuid(), + 'fixed_ips': fixed_ips, + 'subnets': subnets} + + routes = [] + if extra_routes: + routes = [{'destination': '8.8.8.0/24', 'nexthop': '19.4.4.4'}] + + router = { + 'id': router_id, + 'distributed': False, + l3_constants.INTERFACE_KEY: [], + 'routes': routes, + 'gw_port': ex_gw_port} + + if enable_floating_ip: + router[l3_constants.FLOATINGIP_KEY] = [{ + 'id': _uuid(), + 'port_id': _uuid(), + 'status': 'DOWN', + 'floating_ip_address': '19.4.4.2', + 'fixed_ip_address': '10.0.0.1'}] + + router_append_interface(router, count=num_internal_ports, + ip_version=ip_version, dual_stack=dual_stack) + if enable_ha: + router['ha'] = True + router['ha_vr_id'] = 1 + router[l3_constants.HA_INTERFACE_KEY] = (get_ha_interface()) + + if enable_snat is not None: + router['enable_snat'] = enable_snat + return router + + +def get_subnet_id(port): + return port['fixed_ips'][0]['subnet_id'] + + +def router_append_interface(router, count=1, ip_version=4, ra_mode=None, + addr_mode=None, dual_stack=False): + interfaces = router[l3_constants.INTERFACE_KEY] + current = sum( + [netaddr.IPNetwork(subnet['cidr']).version == ip_version + for p in interfaces for subnet in p['subnets']]) + + mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') + mac_address.dialect = netaddr.mac_unix + for i in range(current, current + count): + fixed_ips = [] + subnets = [] + for loop_version in (4, 6): + if loop_version == 4 and (ip_version == 4 or dual_stack): + ip_pool = '35.4.%i.4' + cidr_pool = '35.4.%i.0/24' + prefixlen = 24 + gw_pool = '35.4.%i.1' + elif loop_version == 6 and (ip_version == 6 or dual_stack): + ip_pool = 'fd01:%x:1::6' + cidr_pool = 'fd01:%x:1::/64' + prefixlen = 64 + gw_pool = 'fd01:%x:1::1' + else: + continue + subnet_id = _uuid() + fixed_ips.append({'ip_address': ip_pool % i, + 'subnet_id': subnet_id, + 'prefixlen': prefixlen}) + subnets.append({'id': subnet_id, + 'cidr': cidr_pool % i, + 'gateway_ip': gw_pool % i, + 'ipv6_ra_mode': ra_mode, + 'ipv6_address_mode': addr_mode}) + if not fixed_ips: + raise ValueError("Invalid ip_version: %s" % ip_version) + + interfaces.append( + {'id': _uuid(), + 'network_id': _uuid(), + 'admin_state_up': True, + 'fixed_ips': fixed_ips, + 'mac_address': str(mac_address), + 'subnets': subnets}) + mac_address.value += 1 + + +def router_append_subnet(router, count=1, ip_version=4, + ipv6_subnet_modes=None, interface_id=None): + if ip_version == 6: + subnet_mode_none = {'ra_mode': None, 'address_mode': None} + if not ipv6_subnet_modes: + ipv6_subnet_modes = [subnet_mode_none] * count + elif len(ipv6_subnet_modes) != count: + ipv6_subnet_modes.extend([subnet_mode_none for i in + moves.range(len(ipv6_subnet_modes), + count)]) + + if ip_version == 4: + ip_pool = '35.4.%i.4' + cidr_pool = '35.4.%i.0/24' + prefixlen = 24 + gw_pool = '35.4.%i.1' + elif ip_version == 6: + ip_pool = 'fd01:%x::6' + cidr_pool = 'fd01:%x::/64' + prefixlen = 64 + gw_pool = 'fd01:%x::1' + else: + raise ValueError("Invalid ip_version: %s" % ip_version) + + interfaces = copy.deepcopy(router.get(l3_constants.INTERFACE_KEY, [])) + if interface_id: + try: + interface = next(i for i in interfaces + if i['id'] == interface_id) + except StopIteration: + raise ValueError("interface_id not found") + + fixed_ips, subnets = interface['fixed_ips'], interface['subnets'] + else: + interface = None + fixed_ips, subnets = [], [] + + num_existing_subnets = len(subnets) + for i in moves.range(count): + subnet_id = _uuid() + fixed_ips.append( + {'ip_address': ip_pool % (i + num_existing_subnets), + 'subnet_id': subnet_id, + 'prefixlen': prefixlen}) + subnets.append( + {'id': subnet_id, + 'cidr': cidr_pool % (i + num_existing_subnets), + 'gateway_ip': gw_pool % (i + num_existing_subnets), + 'ipv6_ra_mode': ipv6_subnet_modes[i]['ra_mode'], + 'ipv6_address_mode': ipv6_subnet_modes[i]['address_mode']}) + + if interface: + # Update old interface + index = interfaces.index(interface) + interfaces[index].update({'fixed_ips': fixed_ips, 'subnets': subnets}) + else: + # New interface appended to interfaces list + mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') + mac_address.dialect = netaddr.mac_unix + interfaces.append( + {'id': _uuid(), + 'network_id': _uuid(), + 'admin_state_up': True, + 'mac_address': str(mac_address), + 'fixed_ips': fixed_ips, + 'subnets': subnets}) + + router[l3_constants.INTERFACE_KEY] = interfaces + + +def prepare_ext_gw_test(context, ri, dual_stack=False): + subnet_id = _uuid() + fixed_ips = [{'subnet_id': subnet_id, + 'ip_address': '20.0.0.30', + 'prefixlen': 24}] + subnets = [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}] + if dual_stack: + subnet_id_v6 = _uuid() + fixed_ips.append({'subnet_id': subnet_id_v6, + 'ip_address': '2001:192:168:100::2', + 'prefixlen': 64}) + subnets.append({'id': subnet_id_v6, + 'cidr': '2001:192:168:100::/64', + 'gateway_ip': '2001:192:168:100::1'}) + ex_gw_port = {'fixed_ips': fixed_ips, + 'subnets': subnets, + 'extra_subnets': [{'cidr': '172.16.0.0/24'}], + 'id': _uuid(), + 'network_id': _uuid(), + 'mac_address': 'ca:fe:de:ad:be:ef'} + interface_name = ri.get_external_device_name(ex_gw_port['id']) + + context.device_exists.return_value = True + + return interface_name, ex_gw_port diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index 7788654bf..f2674d82a 100644 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -45,11 +45,12 @@ from neutron.common import config as common_config from neutron.common import constants as l3_constants from neutron.common import utils as common_utils from neutron.openstack.common import uuidutils +from neutron.tests.common import l3_test_common from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.functional.agent.linux import helpers from neutron.tests.functional import base -from neutron.tests.unit.agent.l3 import test_agent as test_l3_agent + LOG = logging.getLogger(__name__) _uuid = uuidutils.generate_uuid @@ -115,7 +116,7 @@ class L3AgentTestFramework(base.BaseSudoTestCase): enable_fip = False extra_routes = False - return test_l3_agent.prepare_router_data(ip_version=ip_version, + return l3_test_common.prepare_router_data(ip_version=ip_version, enable_snat=enable_snat, enable_floating_ip=enable_fip, enable_ha=enable_ha, @@ -145,7 +146,7 @@ class L3AgentTestFramework(base.BaseSudoTestCase): ip_version=4, ipv6_subnet_modes=None, interface_id=None): - return test_l3_agent.router_append_subnet(router, count, + return l3_test_common.router_append_subnet(router, count, ip_version, ipv6_subnet_modes, interface_id) def _namespace_exists(self, namespace): @@ -741,8 +742,8 @@ class L3HATestFramework(L3AgentTestFramework): router_info_2 = copy.deepcopy(router_info) router_info_2[l3_constants.HA_INTERFACE_KEY] = ( - test_l3_agent.get_ha_interface(ip='169.254.192.2', - mac='22:22:22:22:22:22')) + l3_test_common.get_ha_interface(ip='169.254.192.2', + mac='22:22:22:22:22:22')) get_ns_name.return_value = "%s%s%s" % ( namespaces.RouterNamespace._get_ns_name(router_info_2['id']), @@ -1038,7 +1039,7 @@ class TestDvrRouter(L3AgentTestFramework): def generate_dvr_router_info( self, enable_ha=False, enable_snat=False, **kwargs): - router = test_l3_agent.prepare_router_data( + router = l3_test_common.prepare_router_data( enable_snat=enable_snat, enable_floating_ip=True, enable_ha=enable_ha, @@ -1239,7 +1240,7 @@ class TestDvrRouter(L3AgentTestFramework): # existing ports on the the uplinked subnet, the ARP # cache is properly populated. self.agent.conf.agent_mode = 'dvr_snat' - router_info = test_l3_agent.prepare_router_data() + router_info = l3_test_common.prepare_router_data() router_info['distributed'] = True expected_neighbor = '35.4.1.10' port_data = { diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 143c659dd..1c3b62ce8 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -22,13 +22,13 @@ import mock import netaddr from oslo_log import log import oslo_messaging -from six import moves 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 config as l3_config -from neutron.agent.l3 import dvr_router +from neutron.agent.l3 import dvr_edge_router as dvr_router +from neutron.agent.l3 import dvr_local_router from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import ha from neutron.agent.l3 import legacy_router @@ -49,6 +49,7 @@ from neutron.i18n import _LE from neutron.openstack.common import uuidutils from neutron.plugins.common import constants as p_const from neutron.tests import base +from neutron.tests.common import l3_test_common _uuid = uuidutils.generate_uuid HOSTNAME = 'myhost' @@ -57,228 +58,7 @@ FAKE_ID_2 = _uuid() FIP_PRI = 32768 -class FakeDev(object): - def __init__(self, name): - self.name = name - - -def router_append_interface(router, count=1, ip_version=4, ra_mode=None, - addr_mode=None, dual_stack=False): - interfaces = router[l3_constants.INTERFACE_KEY] - current = sum( - [netaddr.IPNetwork(subnet['cidr']).version == ip_version - for p in interfaces for subnet in p['subnets']]) - - mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') - mac_address.dialect = netaddr.mac_unix - for i in range(current, current + count): - fixed_ips = [] - subnets = [] - for loop_version in (4, 6): - if loop_version == 4 and (ip_version == 4 or dual_stack): - ip_pool = '35.4.%i.4' - cidr_pool = '35.4.%i.0/24' - prefixlen = 24 - gw_pool = '35.4.%i.1' - elif loop_version == 6 and (ip_version == 6 or dual_stack): - ip_pool = 'fd01:%x:1::6' - cidr_pool = 'fd01:%x:1::/64' - prefixlen = 64 - gw_pool = 'fd01:%x:1::1' - else: - continue - subnet_id = _uuid() - fixed_ips.append({'ip_address': ip_pool % i, - 'subnet_id': subnet_id, - 'prefixlen': prefixlen}) - subnets.append({'id': subnet_id, - 'cidr': cidr_pool % i, - 'gateway_ip': gw_pool % i, - 'ipv6_ra_mode': ra_mode, - 'ipv6_address_mode': addr_mode}) - if not fixed_ips: - raise ValueError("Invalid ip_version: %s" % ip_version) - - interfaces.append( - {'id': _uuid(), - 'network_id': _uuid(), - 'admin_state_up': True, - 'fixed_ips': fixed_ips, - 'mac_address': str(mac_address), - 'subnets': subnets}) - mac_address.value += 1 - - -def router_append_subnet(router, count=1, ip_version=4, - ipv6_subnet_modes=None, interface_id=None): - if ip_version == 6: - subnet_mode_none = {'ra_mode': None, 'address_mode': None} - if not ipv6_subnet_modes: - ipv6_subnet_modes = [subnet_mode_none] * count - elif len(ipv6_subnet_modes) != count: - ipv6_subnet_modes.extend([subnet_mode_none for i in - moves.range(len(ipv6_subnet_modes), - count)]) - - if ip_version == 4: - ip_pool = '35.4.%i.4' - cidr_pool = '35.4.%i.0/24' - prefixlen = 24 - gw_pool = '35.4.%i.1' - elif ip_version == 6: - ip_pool = 'fd01:%x::6' - cidr_pool = 'fd01:%x::/64' - prefixlen = 64 - gw_pool = 'fd01:%x::1' - else: - raise ValueError("Invalid ip_version: %s" % ip_version) - - interfaces = copy.deepcopy(router.get(l3_constants.INTERFACE_KEY, [])) - if interface_id: - try: - interface = next(i for i in interfaces - if i['id'] == interface_id) - except StopIteration: - raise ValueError("interface_id not found") - - fixed_ips, subnets = interface['fixed_ips'], interface['subnets'] - else: - interface = None - fixed_ips, subnets = [], [] - - num_existing_subnets = len(subnets) - for i in moves.range(count): - subnet_id = _uuid() - fixed_ips.append( - {'ip_address': ip_pool % (i + num_existing_subnets), - 'subnet_id': subnet_id, - 'prefixlen': prefixlen}) - subnets.append( - {'id': subnet_id, - 'cidr': cidr_pool % (i + num_existing_subnets), - 'gateway_ip': gw_pool % (i + num_existing_subnets), - 'ipv6_ra_mode': ipv6_subnet_modes[i]['ra_mode'], - 'ipv6_address_mode': ipv6_subnet_modes[i]['address_mode']}) - - if interface: - # Update old interface - index = interfaces.index(interface) - interfaces[index].update({'fixed_ips': fixed_ips, 'subnets': subnets}) - else: - # New interface appended to interfaces list - mac_address = netaddr.EUI('ca:fe:de:ad:be:ef') - mac_address.dialect = netaddr.mac_unix - interfaces.append( - {'id': _uuid(), - 'network_id': _uuid(), - 'admin_state_up': True, - 'mac_address': str(mac_address), - 'fixed_ips': fixed_ips, - 'subnets': subnets}) - - router[l3_constants.INTERFACE_KEY] = interfaces - - -def prepare_router_data(ip_version=4, enable_snat=None, num_internal_ports=1, - enable_floating_ip=False, enable_ha=False, - extra_routes=False, dual_stack=False, - v6_ext_gw_with_sub=True, **kwargs): - fixed_ips = [] - subnets = [] - gateway_mac = kwargs.get('gateway_mac', 'ca:fe:de:ad:be:ee') - for loop_version in (4, 6): - if loop_version == 4 and (ip_version == 4 or dual_stack): - ip_address = kwargs.get('ip_address', '19.4.4.4') - prefixlen = 24 - subnet_cidr = kwargs.get('subnet_cidr', '19.4.4.0/24') - gateway_ip = kwargs.get('gateway_ip', '19.4.4.1') - elif (loop_version == 6 and (ip_version == 6 or dual_stack) and - v6_ext_gw_with_sub): - ip_address = kwargs.get('ip_address', 'fd00::4') - prefixlen = 64 - subnet_cidr = kwargs.get('subnet_cidr', 'fd00::/64') - gateway_ip = kwargs.get('gateway_ip', 'fd00::1') - else: - continue - subnet_id = _uuid() - fixed_ips.append({'ip_address': ip_address, - 'subnet_id': subnet_id, - 'prefixlen': prefixlen}) - subnets.append({'id': subnet_id, - 'cidr': subnet_cidr, - 'gateway_ip': gateway_ip}) - if not fixed_ips and v6_ext_gw_with_sub: - raise ValueError("Invalid ip_version: %s" % ip_version) - - router_id = _uuid() - ex_gw_port = {'id': _uuid(), - 'mac_address': gateway_mac, - 'network_id': _uuid(), - 'fixed_ips': fixed_ips, - 'subnets': subnets} - - routes = [] - if extra_routes: - routes = [{'destination': '8.8.8.0/24', 'nexthop': '19.4.4.4'}] - - router = { - 'id': router_id, - 'distributed': False, - l3_constants.INTERFACE_KEY: [], - 'routes': routes, - 'gw_port': ex_gw_port} - - if enable_floating_ip: - router[l3_constants.FLOATINGIP_KEY] = [{ - 'id': _uuid(), - 'port_id': _uuid(), - 'status': 'DOWN', - 'floating_ip_address': '19.4.4.2', - 'fixed_ip_address': '10.0.0.1'}] - - router_append_interface(router, count=num_internal_ports, - ip_version=ip_version, dual_stack=dual_stack) - if enable_ha: - router['ha'] = True - router['ha_vr_id'] = 1 - router[l3_constants.HA_INTERFACE_KEY] = get_ha_interface() - - if enable_snat is not None: - router['enable_snat'] = enable_snat - return router - - -def _get_subnet_id(port): - return port['fixed_ips'][0]['subnet_id'] - - -#TODO(jschwarz): This is a shared function with both the unit tests -# and the functional tests, and should be moved elsewhere (probably -# neutron/tests/common/). -def get_ha_interface(ip='169.254.192.1', mac='12:34:56:78:2b:5d'): - subnet_id = _uuid() - return {'admin_state_up': True, - 'device_id': _uuid(), - 'device_owner': 'network:router_ha_interface', - 'fixed_ips': [{'ip_address': ip, - 'prefixlen': 18, - 'subnet_id': subnet_id}], - 'id': _uuid(), - 'mac_address': mac, - 'name': u'L3 HA Admin port 0', - 'network_id': _uuid(), - 'status': u'ACTIVE', - 'subnets': [{'cidr': '169.254.192.0/18', - 'gateway_ip': '169.254.255.254', - 'id': subnet_id}], - 'tenant_id': '', - 'agent_id': _uuid(), - 'agent_host': 'aaa', - 'priority': 1} - - class BasicRouterOperationsFramework(base.BaseTestCase): - def setUp(self): super(BasicRouterOperationsFramework, self).setUp() mock.patch('eventlet.spawn').start() @@ -493,7 +273,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): l3_agent.L3NATAgent(HOSTNAME, self.conf) def _test_internal_network_action(self, action): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router_id = router['id'] ri = l3router.RouterInfo(router_id, router, **self.ri_kwargs) port = {'network_id': _uuid(), @@ -525,10 +305,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): return '%s/%s' % (fixed_ip['ip_address'], fixed_ip['prefixlen']) def _test_internal_network_action_dist(self, action): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router_id = router['id'] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter( + ri = dvr_router.DvrEdgeRouter( agent, HOSTNAME, router_id, router, **self.ri_kwargs) subnet_id = _uuid() port = {'network_id': _uuid(), @@ -672,11 +452,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): if router.get('distributed'): agent.conf.agent_mode = 'dvr_snat' agent.host = HOSTNAME - ri = dvr_router.DvrRouter(agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) + ri = dvr_router.DvrEdgeRouter(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() @@ -763,40 +543,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): else: raise Exception("Invalid action %s" % action) - def _prepare_ext_gw_test(self, ri, dual_stack=False): - subnet_id = _uuid() - fixed_ips = [{'subnet_id': subnet_id, - 'ip_address': '20.0.0.30', - 'prefixlen': 24}] - subnets = [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}] - if dual_stack: - subnet_id_v6 = _uuid() - fixed_ips.append({'subnet_id': subnet_id_v6, - 'ip_address': '2001:192:168:100::2', - 'prefixlen': 64}) - subnets.append({'id': subnet_id_v6, - 'cidr': '2001:192:168:100::/64', - 'gateway_ip': '2001:192:168:100::1'}) - ex_gw_port = {'fixed_ips': fixed_ips, - 'subnets': subnets, - 'extra_subnets': [{'cidr': '172.16.0.0/24'}], - 'id': _uuid(), - 'network_id': _uuid(), - 'mac_address': 'ca:fe:de:ad:be:ef'} - interface_name = ri.get_external_device_name(ex_gw_port['id']) - - self.device_exists.return_value = True - - return interface_name, ex_gw_port - def _test_external_gateway_updated(self, dual_stack=False): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.use_ipv6 = False - interface_name, ex_gw_port = self._prepare_ext_gw_test( - ri, dual_stack=dual_stack) + interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test( + self, ri, dual_stack=dual_stack) fake_fip = {'floatingips': [{'id': _uuid(), 'floating_ip_address': '192.168.1.34', @@ -836,15 +588,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def _test_ext_gw_updated_dvr_agent_mode(self, host, agent_mode, expected_call_count): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter(agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) + ri = dvr_router.DvrEdgeRouter(agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) ri.create_snat_namespace() - interface_name, ex_gw_port = self._prepare_ext_gw_test(ri) + interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, + ri) ri._external_gateway_added = mock.Mock() # test agent mode = dvr (compute node) @@ -870,57 +623,57 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'dvr_snat', 1) def test_agent_add_external_gateway(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('add', router) def test_agent_add_external_gateway_dual_stack(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('add', router, dual_stack=True) def test_agent_add_external_gateway_dist(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('add', router) def test_agent_add_external_gateway_dist_dual_stack(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('add', router, dual_stack=True) def test_agent_add_external_gateway_no_subnet(self): - router = prepare_router_data(num_internal_ports=2, - v6_ext_gw_with_sub=False) + router = l3_test_common.prepare_router_data(num_internal_ports=2, + v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub', router) def test_agent_add_external_gateway_no_subnet_with_ipv6_gw(self): - router = prepare_router_data(num_internal_ports=2, - v6_ext_gw_with_sub=False) + router = l3_test_common.prepare_router_data(num_internal_ports=2, + v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub_v6_gw', router) def test_agent_add_external_gateway_dual_stack_no_subnet_w_ipv6_gw(self): - router = prepare_router_data(num_internal_ports=2, - v6_ext_gw_with_sub=False) + router = l3_test_common.prepare_router_data(num_internal_ports=2, + v6_ext_gw_with_sub=False) self._test_external_gateway_action('add_no_sub_v6_gw', router, dual_stack=True) def test_agent_remove_external_gateway(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('remove', router) def test_agent_remove_external_gateway_dual_stack(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) self._test_external_gateway_action('remove', router, dual_stack=True) def test_agent_remove_external_gateway_dist(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('remove', router) def test_agent_remove_external_gateway_dist_dual_stack(self): - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router['distributed'] = True router['gw_port_host'] = HOSTNAME self._test_external_gateway_action('remove', router, dual_stack=True) @@ -957,15 +710,15 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.assertIn(r.rule, expected_rules) def test__map_internal_interfaces(self): - router = prepare_router_data(num_internal_ports=4) - ri = dvr_router.DvrRouter(mock.sentinel.agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) + router = l3_test_common.prepare_router_data(num_internal_ports=4) + ri = dvr_router.DvrEdgeRouter(mock.sentinel.agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) test_port = { 'mac_address': '00:12:23:34:45:56', - 'fixed_ips': [{'subnet_id': _get_subnet_id( + 'fixed_ips': [{'subnet_id': l3_test_common.get_subnet_id( router[l3_constants.INTERFACE_KEY][0]), 'ip_address': '101.12.13.14'}]} internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) @@ -978,106 +731,22 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.assertNotEqual(test_port, res_ip) self.assertIsNone(res_ip) - def test__set_subnet_arp_info(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(num_internal_ports=2) - router['distributed'] = True - ri = dvr_router.DvrRouter( - agent, HOSTNAME, router['id'], router, **self.ri_kwargs) - ports = ri.router.get(l3_constants.INTERFACE_KEY, []) - subnet_id = _get_subnet_id(ports[0]) - test_ports = [{'mac_address': '00:11:22:33:44:55', - 'device_owner': 'network:dhcp', - 'fixed_ips': [{'ip_address': '1.2.3.4', - 'prefixlen': 24, - 'subnet_id': subnet_id}]}] - - self.plugin_api.get_ports_by_subnet.return_value = test_ports - - # Test basic case - ports[0]['subnets'] = [{'id': subnet_id, - 'cidr': '1.2.3.0/24'}] - ri._set_subnet_arp_info(subnet_id) - self.mock_ip_dev.neigh.add.assert_called_once_with( - '1.2.3.4', '00:11:22:33:44:55') - - # Test negative case - router['distributed'] = False - ri._set_subnet_arp_info(subnet_id) - self.mock_ip_dev.neigh.add.never_called() - - def test_add_arp_entry(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(num_internal_ports=2) - router['distributed'] = True - subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0]) - arp_table = {'ip_address': '1.7.23.11', - 'mac_address': '00:11:22:33:44:55', - 'subnet_id': subnet_id} - - payload = {'arp_table': arp_table, 'router_id': router['id']} - agent._router_added(router['id'], router) - agent.add_arp_entry(None, payload) - agent.router_deleted(None, router['id']) - self.mock_ip_dev.neigh.add.assert_called_once_with( - '1.7.23.11', '00:11:22:33:44:55') - - def test_add_arp_entry_no_routerinfo(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(num_internal_ports=2) - subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0]) - arp_table = {'ip_address': '1.7.23.11', - 'mac_address': '00:11:22:33:44:55', - 'subnet_id': subnet_id} - - payload = {'arp_table': arp_table, 'router_id': router['id']} - agent.add_arp_entry(None, payload) - - def test__update_arp_entry_with_no_subnet(self): - ri = dvr_router.DvrRouter( - mock.sentinel.agent, - HOSTNAME, - 'foo_router_id', - {'distributed': True, 'gw_port_host': HOSTNAME}, - **self.ri_kwargs) - with mock.patch.object(l3_agent.ip_lib, 'IPDevice') as f: - ri._update_arp_entry(mock.ANY, mock.ANY, 'foo_subnet_id', 'add') - self.assertFalse(f.call_count) - - def test_del_arp_entry(self): - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(num_internal_ports=2) - router['distributed'] = True - subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0]) - arp_table = {'ip_address': '1.5.25.15', - 'mac_address': '00:44:33:22:11:55', - 'subnet_id': subnet_id} - - payload = {'arp_table': arp_table, 'router_id': router['id']} - agent._router_added(router['id'], router) - # first add the entry - agent.add_arp_entry(None, payload) - # now delete it - agent.del_arp_entry(None, payload) - self.mock_ip_dev.neigh.delete.assert_called_once_with( - '1.5.25.15', '00:44:33:22:11:55') - agent.router_deleted(None, router['id']) - def test_process_cent_router(self): - router = prepare_router_data() + router = l3_test_common.prepare_router_data() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) self._test_process_router(ri, agent) def test_process_dist_router(self): - router = prepare_router_data() + router = l3_test_common.prepare_router_data() agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter(agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) - subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0]) + ri = dvr_router.DvrEdgeRouter(agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) + subnet_id = l3_test_common.get_subnet_id( + router[l3_constants.INTERFACE_KEY][0]) ri.router['distributed'] = True ri.router['_snat_router_interfaces'] = [{ 'fixed_ips': [{'subnet_id': subnet_id, @@ -1178,33 +847,6 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri.add_floating_ip.assert_called_once_with( floating_ips[0], mock.sentinel.interface_name, device) - def test_get_floating_agent_gw_interfaces(self): - fake_network_id = _uuid() - subnet_id = _uuid() - agent_gateway_port = ( - [{'fixed_ips': [{'ip_address': '20.0.0.30', - 'prefixlen': 24, - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], - 'id': _uuid(), - 'binding:host_id': 'myhost', - 'device_owner': 'network:floatingip_agent_gateway', - 'network_id': fake_network_id, - 'mac_address': 'ca:fe:de:ad:be:ef'}] - ) - - router = prepare_router_data(enable_snat=True) - router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port - router['distributed'] = True - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter( - agent, HOSTNAME, router['id'], router, **self.ri_kwargs) - self.assertEqual( - agent_gateway_port[0], - ri.get_floating_agent_gw_interface(fake_network_id)) - @mock.patch.object(lla.LinkLocalAllocator, '_write') def test_create_dvr_fip_interfaces(self, lla_write): fake_network_id = _uuid() @@ -1229,12 +871,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'mac_address': 'ca:fe:de:ad:be:ef'}] ) - router = prepare_router_data(enable_snat=True) + router = l3_test_common.prepare_router_data(enable_snat=True) router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter( + ri = dvr_router.DvrEdgeRouter( agent, HOSTNAME, router['id'], router, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') @@ -1243,7 +885,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri.fip_ns.subscribe = mock.Mock() ri.fip_ns.agent_router_gateway = mock.Mock() - with mock.patch.object(ri, 'get_floating_ips') as fips,\ + with mock.patch.object(ri, 'get_floating_ips') as fips, \ mock.patch.object(ri, 'get_floating_agent_gw_interface' ) as fip_gw_port: fips.return_value = fake_floatingips @@ -1278,12 +920,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'mac_address': 'ca:fe:de:ad:be:ef'}] ) - router = prepare_router_data(enable_snat=True) + router = l3_test_common.prepare_router_data(enable_snat=True) router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port router['distributed'] = True agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter( + ri = dvr_router.DvrEdgeRouter( agent, HOSTNAME, router['id'], router, **self.ri_kwargs) ext_gw_port = ri.router.get('gw_port') ri.fip_ns = agent.get_fip_ns(ext_gw_port['network_id']) @@ -1314,7 +956,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 'port_id': _uuid(), 'host': HOSTNAME}]} - router = prepare_router_data(enable_snat=True) + router = l3_test_common.prepare_router_data(enable_snat=True) router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) @@ -1322,48 +964,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): 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): - fake_floatingips = {'floatingips': [ - {'id': _uuid(), - 'host': HOSTNAME, - 'floating_ip_address': '15.1.2.3', - 'fixed_ip_address': '192.168.0.1', - 'floating_network_id': mock.sentinel.ext_net_id, - 'port_id': _uuid()}, - {'id': _uuid(), - 'host': 'some-other-host', - 'floating_ip_address': '15.1.2.4', - 'fixed_ip_address': '192.168.0.10', - 'floating_network_id': mock.sentinel.ext_net_id, - 'port_id': _uuid()}]} - - router = prepare_router_data(enable_snat=True) - router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] - router['distributed'] = True - agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter(agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) - ri.iptables_manager.ipv4['nat'] = mock.MagicMock() - ri.dist_fip_count = 0 - fip_ns = agent.get_fip_ns(mock.sentinel.ext_net_id) - subnet_id = _uuid() - fip_ns.agent_gateway_port = ( - {'fixed_ips': [{'ip_address': '20.0.0.30', - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], - 'id': _uuid(), - 'network_id': _uuid(), - 'mac_address': 'ca:fe:de:ad:be:ef'} - ) - def test_process_router_snat_disabled(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(enable_snat=True) + router = l3_test_common.prepare_router_data(enable_snat=True) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT @@ -1390,7 +993,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_snat_enabled(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(enable_snat=False) + router = l3_test_common.prepare_router_data(enable_snat=False) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process without NAT @@ -1417,13 +1020,13 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_interface_added(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process(agent) # Add an interface and reprocess - router_append_interface(router) + l3_test_common.router_append_interface(router) # Reassign the router object to RouterInfo ri.router = router ri.process(agent) @@ -1432,7 +1035,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def _test_process_ipv6_only_or_dual_stack_gw(self, dual_stack=False): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(ip_version=6, dual_stack=dual_stack) + router = l3_test_common.prepare_router_data(ip_version=6, + dual_stack=dual_stack) # Get NAT rules without the gw_port gw_port = router['gw_port'] router['gw_port'] = None @@ -1475,8 +1079,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): ri.process(agent) orig_nat_rules = ri.iptables_manager.ipv4['nat'].rules[:] # Add an IPv6 interface and reprocess - router_append_interface(router, count=1, ip_version=6, ra_mode=ra_mode, - addr_mode=addr_mode) + l3_test_common.router_append_interface(router, count=1, + ip_version=6, ra_mode=ra_mode, + addr_mode=addr_mode) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) # IPv4 NAT rules should not be changed by adding an IPv6 interface @@ -1502,8 +1107,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface with len(ipv6_subnet_modes) subnets # and reprocess - router_append_subnet(router, count=len(ipv6_subnet_modes), - ip_version=6, ipv6_subnet_modes=ipv6_subnet_modes) + l3_test_common.router_append_subnet( + router, + count=len(ipv6_subnet_modes), + ip_version=6, + ipv6_subnet_modes=ipv6_subnet_modes) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) return ri @@ -1523,7 +1131,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.assertEqual(expected_calls, self.external_process.mock_calls) def test_process_router_ipv6_interface_added(self): - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_interface_added(router) self._assert_ri_process_enabled(ri, 'radvd') # Expect radvd configured without prefix @@ -1531,7 +1139,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.utils_replace_file.call_args[0][1].split()) def test_process_router_ipv6_slaac_interface_added(self): - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_interface_added( router, ra_mode=l3_constants.IPV6_SLAAC) self._assert_ri_process_enabled(ri, 'radvd') @@ -1540,7 +1148,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.utils_replace_file.call_args[0][1].split()) def test_process_router_ipv6_subnets_added(self): - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes=[ {'ra_mode': l3_constants.IPV6_SLAAC, 'address_mode': l3_constants.IPV6_SLAAC}, @@ -1557,14 +1165,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_ipv6_subnets_added_to_existing_port(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add the first subnet on a new interface - router_append_subnet(router, count=1, ip_version=6, ipv6_subnet_modes=[ - {'ra_mode': l3_constants.IPV6_SLAAC, - 'address_mode': l3_constants.IPV6_SLAAC}]) + l3_test_common.router_append_subnet( + router, count=1, + ip_version=6, ipv6_subnet_modes=[ + {'ra_mode': l3_constants.IPV6_SLAAC, + 'address_mode': l3_constants.IPV6_SLAAC}]) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri, 'radvd') radvd_config = self.utils_replace_file.call_args[0][1].split() @@ -1578,9 +1188,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.utils_replace_file.reset_mock() # Add the second subnet on the same interface interface_id = router[l3_constants.INTERFACE_KEY][1]['id'] - router_append_subnet(router, count=1, ip_version=6, ipv6_subnet_modes=[ - {'ra_mode': l3_constants.IPV6_SLAAC, - 'address_mode': l3_constants.IPV6_SLAAC}], + l3_test_common.router_append_subnet( + router, count=1, + ip_version=6, + ipv6_subnet_modes=[ + {'ra_mode': l3_constants.IPV6_SLAAC, + 'address_mode': l3_constants.IPV6_SLAAC}], interface_id=interface_id) self._process_router_instance_for_agent(agent, ri, router) # radvd should have been enabled again and the interface @@ -1594,21 +1207,21 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_ipv6v4_interface_added(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT ri.process(agent) # Add an IPv4 and IPv6 interface and reprocess - router_append_interface(router, count=1, ip_version=4) - router_append_interface(router, count=1, ip_version=6) + l3_test_common.router_append_interface(router, count=1, ip_version=4) + l3_test_common.router_append_interface(router, count=1, ip_version=6) # Reassign the router object to RouterInfo self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri, 'radvd') def test_process_router_interface_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # Process with NAT @@ -1623,12 +1236,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_ipv6_interface_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) 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) + l3_test_common.router_append_interface(router, count=1, ip_version=6) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri, 'radvd') # Reset the calls so we can check for disable radvd @@ -1641,16 +1254,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_ipv6_subnet_removed(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) agent.external_gateway_added = mock.Mock() self._process_router_instance_for_agent(agent, ri, router) # Add an IPv6 interface with two subnets and reprocess - router_append_subnet(router, count=2, ip_version=6, - ipv6_subnet_modes=([ - {'ra_mode': l3_constants.IPV6_SLAAC, - 'address_mode': l3_constants.IPV6_SLAAC} - ] * 2)) + l3_test_common.router_append_subnet( + router, count=2, ip_version=6, + ipv6_subnet_modes=([{'ra_mode': l3_constants.IPV6_SLAAC, + 'address_mode': l3_constants.IPV6_SLAAC}] + * 2)) self._process_router_instance_for_agent(agent, ri, router) self._assert_ri_process_enabled(ri, 'radvd') # Reset mocks to check for modified radvd config @@ -1673,7 +1286,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_internal_network_added_unexpected_error(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() with mock.patch.object( @@ -1698,7 +1311,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_internal_network_removed_unexpected_error(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) ri.external_gateway_added = mock.Mock() # add an internal port @@ -1731,7 +1344,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): with mock.patch.object( agent.plugin_rpc, 'update_floatingip_statuses' ) as mock_update_fip_status: - router = prepare_router_data(num_internal_ports=1) + router = l3_test_common.prepare_router_data(num_internal_ports=1) fip1 = {'id': _uuid(), 'floating_ip_address': '8.8.8.8', 'fixed_ip_address': '7.7.7.7', 'status': 'ACTIVE', 'port_id': router[l3_constants.INTERFACE_KEY][0]['id']} @@ -1753,7 +1366,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() - router = prepare_router_data(num_internal_ports=1) + router = l3_test_common.prepare_router_data(num_internal_ports=1) router[l3_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', @@ -1786,7 +1399,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() - router = prepare_router_data(num_internal_ports=1) + router = l3_test_common.prepare_router_data(num_internal_ports=1) router[l3_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', @@ -1810,7 +1423,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent.plugin_rpc, 'update_floatingip_statuses') as mock_update_fip_status: fip_id = _uuid() - router = prepare_router_data(num_internal_ports=1) + router = l3_test_common.prepare_router_data(num_internal_ports=1) router[l3_constants.FLOATINGIP_KEY] = [ {'id': fip_id, 'floating_ip_address': '8.8.8.8', @@ -1830,7 +1443,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_handle_router_snat_rules_distributed_without_snat_manager(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - ri = dvr_router.DvrRouter( + ri = dvr_router.DvrEdgeRouter( agent, HOSTNAME, 'foo_router_id', @@ -1838,7 +1451,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): **self.ri_kwargs) ri.iptables_manager = mock.Mock() - with mock.patch.object(dvr_router.LOG, 'debug') as log_debug: + with mock.patch.object(dvr_local_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) @@ -1893,15 +1507,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_delete_stale_internal_devices(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - stale_devlist = [FakeDev('qr-a1b2c3d4-e5'), - FakeDev('qr-b2c3d4e5-f6')] + stale_devlist = [l3_test_common.FakeDev('qr-a1b2c3d4-e5'), + l3_test_common.FakeDev('qr-b2c3d4e5-f6')] stale_devnames = [dev.name for dev in stale_devlist] get_devices_return = [] get_devices_return.extend(stale_devlist) self.mock_ip.get_devices.return_value = get_devices_return - router = prepare_router_data(enable_snat=True, num_internal_ports=1) + router = l3_test_common.prepare_router_data(enable_snat=True, + num_internal_ports=1) ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) @@ -1933,10 +1548,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_process_router_delete_stale_external_devices(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - stale_devlist = [FakeDev('qg-a1b2c3d4-e5')] + stale_devlist = [l3_test_common.FakeDev('qg-a1b2c3d4-e5')] stale_devnames = [dev.name for dev in stale_devlist] - router = prepare_router_data(enable_snat=True, num_internal_ports=1) + router = l3_test_common.prepare_router_data(enable_snat=True, + num_internal_ports=1) del router['gw_port'] ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) @@ -1978,8 +1594,9 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): namespace = 'qrouter-bar' self.mock_ip.get_namespaces.return_value = [namespace] - self.mock_ip.get_devices.return_value = [FakeDev('qr-aaaa'), - FakeDev('rfp-aaaa')] + self.mock_ip.get_devices.return_value = [ + l3_test_common.FakeDev('qr-aaaa'), + l3_test_common.FakeDev('rfp-aaaa')] agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) @@ -2268,12 +1885,12 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): def test_create_dvr_gateway(self): agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) - router = prepare_router_data() - ri = dvr_router.DvrRouter(agent, - HOSTNAME, - router['id'], - router, - **self.ri_kwargs) + router = l3_test_common.prepare_router_data() + ri = dvr_router.DvrEdgeRouter(agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) port_id = _uuid() subnet_id = _uuid() @@ -2331,17 +1948,17 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.assertRaises(oslo_messaging.MessagingTimeout, l3_agent.L3NATAgent, HOSTNAME, self.conf) - def _test_external_gateway_removed_ext_gw_port_and_fip(self, fip_ns=False): + def test_external_gateway_removed_ext_gw_port_no_fip_ns(self): self.conf.set_override('state_path', '/tmp') agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) agent.conf.agent_mode = 'dvr_snat' - router = prepare_router_data(num_internal_ports=2) + router = l3_test_common.prepare_router_data(num_internal_ports=2) router['gw_port_host'] = HOSTNAME self.mock_driver.unplug.reset_mock() external_net_id = router['gw_port']['network_id'] - ri = dvr_router.DvrRouter( + ri = dvr_router.DvrEdgeRouter( agent, HOSTNAME, router['id'], router, **self.ri_kwargs) ri.remove_floating_ip = mock.Mock() agent._fetch_external_net_id = mock.Mock(return_value=external_net_id) @@ -2351,54 +1968,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): nat = ri.iptables_manager.ipv4['nat'] nat.clear_rules_by_tag = mock.Mock() nat.add_rule = mock.Mock() - if fip_ns: - ri.fip_ns = agent.get_fip_ns(external_net_id) - subnet_id = _uuid() - ri.fip_ns.agent_gateway_port = { - 'fixed_ips': [{ - 'ip_address': '20.0.0.30', - 'prefixlen': 24, - 'subnet_id': subnet_id - }], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], - 'id': _uuid(), - 'network_id': external_net_id, - 'mac_address': 'ca:fe:de:ad:be:ef'} - - vm_floating_ip = '19.4.4.2' - ri.floating_ips_dict[vm_floating_ip] = FIP_PRI - ri.dist_fip_count = 1 - ri.rtr_fip_subnet = ri.fip_ns.local_subnets.allocate(ri.router_id) - _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() - self.mock_ip.get_devices.return_value = [ - FakeDev(ri.fip_ns.get_ext_device_name(_uuid()))] - self.mock_ip_dev.addr.list.return_value = [ - {'cidr': vm_floating_ip + '/32'}, - {'cidr': '19.4.4.1/24'}] - self.device_exists.return_value = True - fip_ns = ri.fip_ns 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, - '19.4.4.2/32') - else: - self.assertFalse(ri.remove_floating_ip.called) - - def test_external_gateway_removed_ext_gw_port_and_fip(self): - self._test_external_gateway_removed_ext_gw_port_and_fip(fip_ns=True) - - def test_external_gateway_removed_ext_gw_port_no_fip_ns(self): - self._test_external_gateway_removed_ext_gw_port_and_fip(fip_ns=False) + self.assertFalse(ri.remove_floating_ip.called) def test_spawn_radvd(self): - router = prepare_router_data(ip_version=6) + router = l3_test_common.prepare_router_data(ip_version=6) conffile = '/fake/radvd.conf' pidfile = '/fake/radvd.pid' @@ -2424,7 +2003,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router['id'], namespaces.RouterNamespace._get_ns_name(router['id']), agent.process_monitor, - FakeDev) + l3_test_common.FakeDev) radvd.enable(router['_interfaces']) cmd = execute.call_args[0][0] @@ -2452,7 +2031,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): for mode_list in mode_combos: ipv6_subnet_modes = [{'ra_mode': mode, 'address_mode': mode} for mode in mode_list] - router = prepare_router_data() + router = l3_test_common.prepare_router_data() ri = self._process_router_ipv6_subnet_added(router, ipv6_subnet_modes) diff --git a/neutron/tests/unit/agent/l3/test_dvr_local_router.py b/neutron/tests/unit/agent/l3/test_dvr_local_router.py new file mode 100644 index 000000000..1e81c8d48 --- /dev/null +++ b/neutron/tests/unit/agent/l3/test_dvr_local_router.py @@ -0,0 +1,573 @@ +# Copyright (c) 2015 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 mock +import netaddr + +from oslo_log import log + +from neutron.agent.common import config as agent_config +from neutron.agent.l3 import agent as l3_agent +from neutron.agent.l3 import config as l3_config +from neutron.agent.l3 import dvr_local_router as dvr_router +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.linux import external_process +from neutron.agent.linux import interface +from neutron.agent.linux import ip_lib +from neutron.callbacks import manager +from neutron.callbacks import registry +from neutron.common import config as base_config +from neutron.common import constants as l3_constants +from neutron.common import utils as common_utils +from neutron.openstack.common import uuidutils +from neutron.tests import base +from neutron.tests.common import l3_test_common + +_uuid = uuidutils.generate_uuid +FIP_PRI = 32768 +HOSTNAME = 'myhost' + + +class TestDvrRouterOperations(base.BaseTestCase): + + def setUp(self): + super(TestDvrRouterOperations, self).setUp() + mock.patch('eventlet.spawn').start() + self.conf = agent_config.setup_conf() + self.conf.register_opts(base_config.core_opts) + log.register_options(self.conf) + self.conf.register_opts(agent_config.AGENT_STATE_OPTS, 'AGENT') + self.conf.register_opts(l3_config.OPTS) + self.conf.register_opts(ha.OPTS) + agent_config.register_interface_driver_opts_helper(self.conf) + agent_config.register_use_namespaces_opts_helper(self.conf) + agent_config.register_process_monitor_opts(self.conf) + self.conf.register_opts(interface.OPTS) + self.conf.register_opts(external_process.OPTS) + self.conf.set_override('router_id', 'fake_id') + self.conf.set_override('interface_driver', + 'neutron.agent.linux.interface.NullDriver') + self.conf.set_override('send_arp_for_ha', 1) + self.conf.set_override('state_path', '') + + self.device_exists_p = mock.patch( + 'neutron.agent.linux.ip_lib.device_exists') + self.device_exists = self.device_exists_p.start() + + self.ensure_dir = mock.patch('neutron.agent.linux.utils' + '.ensure_dir').start() + + mock.patch('neutron.agent.linux.keepalived.KeepalivedManager' + '.get_full_config_file_path').start() + + self.utils_exec_p = mock.patch( + 'neutron.agent.linux.utils.execute') + self.utils_exec = self.utils_exec_p.start() + + self.utils_replace_file_p = mock.patch( + 'neutron.agent.linux.utils.replace_file') + self.utils_replace_file = self.utils_replace_file_p.start() + + self.external_process_p = mock.patch( + 'neutron.agent.linux.external_process.ProcessManager') + self.external_process = self.external_process_p.start() + self.process_monitor = mock.patch( + 'neutron.agent.linux.external_process.ProcessMonitor').start() + + self.send_adv_notif_p = mock.patch( + 'neutron.agent.linux.ip_lib.send_ip_addr_adv_notif') + self.send_adv_notif = self.send_adv_notif_p.start() + + self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver') + driver_cls = self.dvr_cls_p.start() + self.mock_driver = mock.MagicMock() + self.mock_driver.DEV_NAME_LEN = ( + interface.LinuxInterfaceDriver.DEV_NAME_LEN) + driver_cls.return_value = self.mock_driver + + self.ip_cls_p = mock.patch('neutron.agent.linux.ip_lib.IPWrapper') + ip_cls = self.ip_cls_p.start() + self.mock_ip = mock.MagicMock() + ip_cls.return_value = self.mock_ip + + ip_rule = mock.patch('neutron.agent.linux.ip_lib.IPRule').start() + self.mock_rule = mock.MagicMock() + ip_rule.return_value = self.mock_rule + + ip_dev = mock.patch('neutron.agent.linux.ip_lib.IPDevice').start() + self.mock_ip_dev = mock.MagicMock() + ip_dev.return_value = self.mock_ip_dev + + self.l3pluginApi_cls_p = mock.patch( + 'neutron.agent.l3.agent.L3PluginApi') + l3pluginApi_cls = self.l3pluginApi_cls_p.start() + self.plugin_api = mock.MagicMock() + l3pluginApi_cls.return_value = self.plugin_api + + self.looping_call_p = mock.patch( + 'neutron.openstack.common.loopingcall.FixedIntervalLoopingCall') + self.looping_call_p.start() + + subnet_id_1 = _uuid() + subnet_id_2 = _uuid() + self.snat_ports = [{'subnets': [{'cidr': '152.2.0.0/16', + 'gateway_ip': '152.2.0.1', + 'id': subnet_id_1}], + 'network_id': _uuid(), + 'device_owner': 'network:router_centralized_snat', + 'mac_address': 'fa:16:3e:80:8d:80', + 'fixed_ips': [{'subnet_id': subnet_id_1, + 'ip_address': '152.2.0.13', + 'prefixlen': 16}], + 'id': _uuid(), 'device_id': _uuid()}, + {'subnets': [{'cidr': '152.10.0.0/16', + 'gateway_ip': '152.10.0.1', + 'id': subnet_id_2}], + 'network_id': _uuid(), + 'device_owner': 'network:router_centralized_snat', + 'mac_address': 'fa:16:3e:80:8d:80', + 'fixed_ips': [{'subnet_id': subnet_id_2, + 'ip_address': '152.10.0.13', + 'prefixlen': 16}], + 'id': _uuid(), 'device_id': _uuid()}] + + self.ri_kwargs = {'agent_conf': self.conf, + 'interface_driver': self.mock_driver} + + self._callback_manager = manager.CallbacksManager() + mock.patch.object(registry, '_get_callback_manager', + return_value=self._callback_manager).start() + + def _create_router(self, router=None, **kwargs): + agent_conf = mock.Mock() + self.router_id = _uuid() + if not router: + router = mock.MagicMock() + return dvr_router.DvrLocalRouter(mock.sentinel.agent, + mock.sentinel.myhost, + self.router_id, + router, + agent_conf, + mock.sentinel.interface_driver, + **kwargs) + + def test_get_floating_ips_dvr(self): + router = mock.MagicMock() + router.get.return_value = [{'host': mock.sentinel.myhost}, + {'host': mock.sentinel.otherhost}] + ri = self._create_router(router) + + fips = ri.get_floating_ips() + + self.assertEqual([{'host': mock.sentinel.myhost}], fips) + + @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'IPRule') + def test_floating_ip_added_dist(self, mIPRule, mIPDevice, mock_adv_notif): + router = mock.MagicMock() + ri = self._create_router(router) + ext_net_id = _uuid() + subnet_id = _uuid() + agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', + 'prefixlen': 24, + 'subnet_id': subnet_id}], + 'subnets': [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'network_id': ext_net_id, + 'mac_address': 'ca:fe:de:ad:be:ef'} + + fip = {'id': _uuid(), + 'host': HOSTNAME, + 'floating_ip_address': '15.1.2.3', + 'fixed_ip_address': '192.168.0.1', + 'floating_network_id': ext_net_id, + 'port_id': _uuid()} + ri.fip_ns = mock.Mock() + ri.fip_ns.agent_gateway_port = agent_gw_port + ri.fip_ns.allocate_rule_priority.return_value = FIP_PRI + ri.rtr_fip_subnet = lla.LinkLocalAddressPair('169.254.30.42/31') + ri.dist_fip_count = 0 + ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) + ri.floating_ip_added_dist(fip, ip_cidr) + mIPRule().rule.add.assert_called_with('192.168.0.1', 16, FIP_PRI) + self.assertEqual(1, ri.dist_fip_count) + # TODO(mrsmith): add more asserts + + @mock.patch.object(ip_lib, 'IPWrapper') + @mock.patch.object(ip_lib, 'IPDevice') + @mock.patch.object(ip_lib, 'IPRule') + def test_floating_ip_removed_dist(self, mIPRule, mIPDevice, mIPWrapper): + router = mock.MagicMock() + ri = self._create_router(router) + + subnet_id = _uuid() + agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', + 'prefixlen': 24, + 'subnet_id': subnet_id}], + 'subnets': [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'network_id': _uuid(), + 'mac_address': 'ca:fe:de:ad:be:ef'} + fip_cidr = '11.22.33.44/24' + + ri.dist_fip_count = 2 + ri.fip_ns = mock.Mock() + ri.fip_ns.get_name.return_value = 'fip_ns_name' + ri.floating_ips_dict['11.22.33.44'] = FIP_PRI + ri.fip_2_rtr = '11.22.33.42' + ri.rtr_2_fip = '11.22.33.40' + ri.fip_ns.agent_gateway_port = agent_gw_port + s = lla.LinkLocalAddressPair('169.254.30.42/31') + ri.rtr_fip_subnet = s + ri.floating_ip_removed_dist(fip_cidr) + mIPRule().rule.delete.assert_called_with( + str(netaddr.IPNetwork(fip_cidr).ip), 16, FIP_PRI) + mIPDevice().route.delete_route.assert_called_with(fip_cidr, str(s.ip)) + self.assertFalse(ri.fip_ns.unsubscribe.called) + + ri.dist_fip_count = 1 + ri.rtr_fip_subnet = lla.LinkLocalAddressPair('15.1.2.3/32') + _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() + fip_ns = ri.fip_ns + + ri.floating_ip_removed_dist(fip_cidr) + + self.assertTrue(fip_ns.destroyed) + mIPWrapper().del_veth.assert_called_once_with( + fip_ns.get_int_device_name(router['id'])) + mIPDevice().route.delete_gateway.assert_called_once_with( + str(fip_to_rtr.ip), table=16) + fip_ns.unsubscribe.assert_called_once_with(ri.router_id) + + def _test_add_floating_ip(self, ri, fip, is_failure): + ri._add_fip_addr_to_device = mock.Mock(return_value=is_failure) + ri.floating_ip_added_dist = mock.Mock() + + result = ri.add_floating_ip(fip, + mock.sentinel.interface_name, + mock.sentinel.device) + ri._add_fip_addr_to_device.assert_called_once_with( + fip, mock.sentinel.device) + return result + + def test_add_floating_ip(self): + ri = self._create_router(mock.MagicMock()) + ip = '15.1.2.3' + fip = {'floating_ip_address': ip} + result = self._test_add_floating_ip(ri, fip, True) + ri.floating_ip_added_dist.assert_called_once_with(fip, ip + '/32') + self.assertEqual(l3_constants.FLOATINGIP_STATUS_ACTIVE, result) + + def test_add_floating_ip_error(self): + ri = self._create_router(mock.MagicMock()) + result = self._test_add_floating_ip( + ri, {'floating_ip_address': '15.1.2.3'}, False) + self.assertFalse(ri.floating_ip_added_dist.called) + self.assertEqual(l3_constants.FLOATINGIP_STATUS_ERROR, result) + + @mock.patch.object(router_info.RouterInfo, 'remove_floating_ip') + def test_remove_floating_ip(self, super_remove_floating_ip): + ri = self._create_router(mock.MagicMock()) + ri.floating_ip_removed_dist = mock.Mock() + + ri.remove_floating_ip(mock.sentinel.device, mock.sentinel.ip_cidr) + + super_remove_floating_ip.assert_called_once_with( + mock.sentinel.device, mock.sentinel.ip_cidr) + ri.floating_ip_removed_dist.assert_called_once_with( + mock.sentinel.ip_cidr) + + def test__get_internal_port(self): + ri = self._create_router() + port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} + router_ports = [port] + ri.router.get.return_value = router_ports + self.assertEqual(port, ri._get_internal_port(mock.sentinel.subnet_id)) + + def test__get_internal_port_not_found(self): + ri = self._create_router() + port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} + router_ports = [port] + ri.router.get.return_value = router_ports + self.assertEqual(None, ri._get_internal_port(mock.sentinel.subnet_id2)) + + def test__get_snat_idx_ipv4(self): + ip_cidr = '101.12.13.00/24' + ri = self._create_router(mock.MagicMock()) + snat_idx = ri._get_snat_idx(ip_cidr) + # 0x650C0D00 is numerical value of 101.12.13.00 + self.assertEqual(0x650C0D00, snat_idx) + + def test__get_snat_idx_ipv6(self): + ip_cidr = '2620:0:a03:e100::/64' + ri = self._create_router(mock.MagicMock()) + snat_idx = ri._get_snat_idx(ip_cidr) + # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr + self.assertEqual(0x3D345705, snat_idx) + + def test__get_snat_idx_ipv6_below_32768(self): + ip_cidr = 'd488::/30' + # crc32 of this ip_cidr is 0x1BD7 + ri = self._create_router(mock.MagicMock()) + snat_idx = ri._get_snat_idx(ip_cidr) + # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6 + self.assertEqual(0x40001BD6, snat_idx) + + def test__set_subnet_arp_info(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + router = l3_test_common.prepare_router_data(num_internal_ports=2) + router['distributed'] = True + ri = dvr_router.DvrLocalRouter( + agent, HOSTNAME, router['id'], router, **self.ri_kwargs) + ports = ri.router.get(l3_constants.INTERFACE_KEY, []) + subnet_id = l3_test_common.get_subnet_id(ports[0]) + test_ports = [{'mac_address': '00:11:22:33:44:55', + 'device_owner': 'network:dhcp', + 'fixed_ips': [{'ip_address': '1.2.3.4', + 'prefixlen': 24, + 'subnet_id': subnet_id}]}] + + self.plugin_api.get_ports_by_subnet.return_value = test_ports + + # Test basic case + ports[0]['subnets'] = [{'id': subnet_id, + 'cidr': '1.2.3.0/24'}] + ri._set_subnet_arp_info(subnet_id) + self.mock_ip_dev.neigh.add.assert_called_once_with( + '1.2.3.4', '00:11:22:33:44:55') + + # Test negative case + router['distributed'] = False + ri._set_subnet_arp_info(subnet_id) + self.mock_ip_dev.neigh.add.never_called() + + def test_add_arp_entry(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + router = l3_test_common.prepare_router_data(num_internal_ports=2) + router['distributed'] = True + subnet_id = l3_test_common.get_subnet_id( + router[l3_constants.INTERFACE_KEY][0]) + arp_table = {'ip_address': '1.7.23.11', + 'mac_address': '00:11:22:33:44:55', + 'subnet_id': subnet_id} + + payload = {'arp_table': arp_table, 'router_id': router['id']} + agent._router_added(router['id'], router) + agent.add_arp_entry(None, payload) + agent.router_deleted(None, router['id']) + self.mock_ip_dev.neigh.add.assert_called_once_with( + '1.7.23.11', '00:11:22:33:44:55') + + def test_add_arp_entry_no_routerinfo(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + router = l3_test_common.prepare_router_data(num_internal_ports=2) + subnet_id = l3_test_common.get_subnet_id( + router[l3_constants.INTERFACE_KEY][0]) + arp_table = {'ip_address': '1.7.23.11', + 'mac_address': '00:11:22:33:44:55', + 'subnet_id': subnet_id} + + payload = {'arp_table': arp_table, 'router_id': router['id']} + agent.add_arp_entry(None, payload) + + def test__update_arp_entry_with_no_subnet(self): + ri = dvr_router.DvrLocalRouter( + mock.sentinel.agent, + HOSTNAME, + 'foo_router_id', + {'distributed': True, 'gw_port_host': HOSTNAME}, + **self.ri_kwargs) + with mock.patch.object(l3_agent.ip_lib, 'IPDevice') as f: + ri._update_arp_entry(mock.ANY, mock.ANY, 'foo_subnet_id', 'add') + self.assertFalse(f.call_count) + + def test_del_arp_entry(self): + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + router = l3_test_common.prepare_router_data(num_internal_ports=2) + router['distributed'] = True + subnet_id = l3_test_common.get_subnet_id( + router[l3_constants.INTERFACE_KEY][0]) + arp_table = {'ip_address': '1.5.25.15', + 'mac_address': '00:44:33:22:11:55', + 'subnet_id': subnet_id} + + payload = {'arp_table': arp_table, 'router_id': router['id']} + agent._router_added(router['id'], router) + # first add the entry + agent.add_arp_entry(None, payload) + # now delete it + agent.del_arp_entry(None, payload) + self.mock_ip_dev.neigh.delete.assert_called_once_with( + '1.5.25.15', '00:44:33:22:11:55') + agent.router_deleted(None, router['id']) + + def test_get_floating_agent_gw_interfaces(self): + fake_network_id = _uuid() + subnet_id = _uuid() + agent_gateway_port = ( + [{'fixed_ips': [{'ip_address': '20.0.0.30', + 'prefixlen': 24, + 'subnet_id': subnet_id}], + 'subnets': [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'binding:host_id': 'myhost', + 'device_owner': 'network:floatingip_agent_gateway', + 'network_id': fake_network_id, + 'mac_address': 'ca:fe:de:ad:be:ef'}] + ) + + router = l3_test_common.prepare_router_data(enable_snat=True) + router[l3_constants.FLOATINGIP_AGENT_INTF_KEY] = agent_gateway_port + router['distributed'] = True + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ri = dvr_router.DvrLocalRouter( + agent, HOSTNAME, router['id'], router, **self.ri_kwargs) + self.assertEqual( + agent_gateway_port[0], + ri.get_floating_agent_gw_interface(fake_network_id)) + + def test_process_router_dist_floating_ip_add(self): + fake_floatingips = {'floatingips': [ + {'id': _uuid(), + 'host': HOSTNAME, + 'floating_ip_address': '15.1.2.3', + 'fixed_ip_address': '192.168.0.1', + 'floating_network_id': mock.sentinel.ext_net_id, + 'port_id': _uuid()}, + {'id': _uuid(), + 'host': 'some-other-host', + 'floating_ip_address': '15.1.2.4', + 'fixed_ip_address': '192.168.0.10', + 'floating_network_id': mock.sentinel.ext_net_id, + 'port_id': _uuid()}]} + + router = l3_test_common.prepare_router_data(enable_snat=True) + router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips'] + router['distributed'] = True + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ri = dvr_router.DvrLocalRouter(agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) + ri.iptables_manager.ipv4['nat'] = mock.MagicMock() + ri.dist_fip_count = 0 + fip_ns = agent.get_fip_ns(mock.sentinel.ext_net_id) + subnet_id = _uuid() + fip_ns.agent_gateway_port = ( + {'fixed_ips': [{'ip_address': '20.0.0.30', + 'subnet_id': subnet_id}], + 'subnets': [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'network_id': _uuid(), + 'mac_address': 'ca:fe:de:ad:be:ef'} + ) + + def _test_ext_gw_updated_dvr_agent_mode(self, host, + agent_mode, expected_call_count): + router = l3_test_common.prepare_router_data(num_internal_ports=2) + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ri = dvr_router.DvrLocalRouter(agent, + HOSTNAME, + router['id'], + router, + **self.ri_kwargs) + + interface_name, ex_gw_port = l3_test_common.prepare_ext_gw_test(self, + ri) + ri._external_gateway_added = mock.Mock() + + # test agent mode = dvr (compute node) + router['gw_port_host'] = host + agent.conf.agent_mode = agent_mode + + ri.external_gateway_updated(ex_gw_port, interface_name) + # no gateway should be added on dvr node + self.assertEqual(expected_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 + self._test_ext_gw_updated_dvr_agent_mode('any-foo', 'dvr', 0) + + def test_ext_gw_updated_dvr_agent_mode_host(self): + # no gateway should be added on dvr node + self._test_ext_gw_updated_dvr_agent_mode(HOSTNAME, + 'dvr', 0) + + def test_external_gateway_removed_ext_gw_port_and_fip(self): + self.conf.set_override('state_path', '/tmp') + + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + agent.conf.agent_mode = 'dvr' + router = l3_test_common.prepare_router_data(num_internal_ports=2) + router['gw_port_host'] = HOSTNAME + self.mock_driver.unplug.reset_mock() + + external_net_id = router['gw_port']['network_id'] + ri = dvr_router.DvrLocalRouter( + agent, HOSTNAME, router['id'], router, **self.ri_kwargs) + ri.remove_floating_ip = mock.Mock() + agent._fetch_external_net_id = mock.Mock(return_value=external_net_id) + ri.ex_gw_port = ri.router['gw_port'] + del ri.router['gw_port'] + ri.fip_ns = None + nat = ri.iptables_manager.ipv4['nat'] + nat.clear_rules_by_tag = mock.Mock() + nat.add_rule = mock.Mock() + + ri.fip_ns = agent.get_fip_ns(external_net_id) + subnet_id = _uuid() + ri.fip_ns.agent_gateway_port = { + 'fixed_ips': [{ + 'ip_address': '20.0.0.30', + 'prefixlen': 24, + 'subnet_id': subnet_id + }], + 'subnets': [{'id': subnet_id, + 'cidr': '20.0.0.0/24', + 'gateway_ip': '20.0.0.1'}], + 'id': _uuid(), + 'network_id': external_net_id, + 'mac_address': 'ca:fe:de:ad:be:ef'} + + vm_floating_ip = '19.4.4.2' + ri.floating_ips_dict[vm_floating_ip] = FIP_PRI + ri.dist_fip_count = 1 + ri.rtr_fip_subnet = ri.fip_ns.local_subnets.allocate(ri.router_id) + _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() + self.mock_ip.get_devices.return_value = [ + l3_test_common.FakeDev(ri.fip_ns.get_ext_device_name(_uuid()))] + self.mock_ip_dev.addr.list.return_value = [ + {'cidr': vm_floating_ip + '/32'}, + {'cidr': '19.4.4.1/24'}] + self.device_exists.return_value = True + + ri.external_gateway_removed( + ri.ex_gw_port, + ri.get_external_device_name(ri.ex_gw_port['id'])) + + ri.remove_floating_ip.assert_called_once_with(self.mock_ip_dev, + '19.4.4.2/32') diff --git a/neutron/tests/unit/agent/l3/test_dvr_router.py b/neutron/tests/unit/agent/l3/test_dvr_router.py deleted file mode 100644 index 8b68478c1..000000000 --- a/neutron/tests/unit/agent/l3/test_dvr_router.py +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright (c) 2015 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 mock -import netaddr - -from neutron.agent.l3 import dvr_router -from neutron.agent.l3 import link_local_allocator as lla -from neutron.agent.l3 import router_info -from neutron.agent.linux import ip_lib -from neutron.common import constants as l3_constants -from neutron.common import utils as common_utils -from neutron.openstack.common import uuidutils -from neutron.tests import base - -_uuid = uuidutils.generate_uuid -FIP_PRI = 32768 -HOSTNAME = 'myhost' - - -class TestDvrRouterOperations(base.BaseTestCase): - def setUp(self): - super(TestDvrRouterOperations, self).setUp() - - def _create_router(self, router=None, **kwargs): - agent_conf = mock.Mock() - self.router_id = _uuid() - if not router: - router = mock.MagicMock() - return dvr_router.DvrRouter(mock.sentinel.agent, - mock.sentinel.myhost, - self.router_id, - router, - agent_conf, - mock.sentinel.interface_driver, - **kwargs) - - def test_get_floating_ips_dvr(self): - router = mock.MagicMock() - router.get.return_value = [{'host': mock.sentinel.myhost}, - {'host': mock.sentinel.otherhost}] - ri = self._create_router(router) - - fips = ri.get_floating_ips() - - self.assertEqual([{'host': mock.sentinel.myhost}], fips) - - @mock.patch.object(ip_lib, 'send_ip_addr_adv_notif') - @mock.patch.object(ip_lib, 'IPDevice') - @mock.patch.object(ip_lib, 'IPRule') - def test_floating_ip_added_dist(self, mIPRule, mIPDevice, mock_adv_notif): - router = mock.MagicMock() - ri = self._create_router(router) - ext_net_id = _uuid() - subnet_id = _uuid() - agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', - 'prefixlen': 24, - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], - 'id': _uuid(), - 'network_id': ext_net_id, - 'mac_address': 'ca:fe:de:ad:be:ef'} - - fip = {'id': _uuid(), - 'host': HOSTNAME, - 'floating_ip_address': '15.1.2.3', - 'fixed_ip_address': '192.168.0.1', - 'floating_network_id': ext_net_id, - 'port_id': _uuid()} - ri.fip_ns = mock.Mock() - ri.fip_ns.agent_gateway_port = agent_gw_port - ri.fip_ns.allocate_rule_priority.return_value = FIP_PRI - ri.rtr_fip_subnet = lla.LinkLocalAddressPair('169.254.30.42/31') - ri.dist_fip_count = 0 - ip_cidr = common_utils.ip_to_cidr(fip['floating_ip_address']) - ri.floating_ip_added_dist(fip, ip_cidr) - mIPRule().rule.add.assert_called_with('192.168.0.1', 16, FIP_PRI) - self.assertEqual(1, ri.dist_fip_count) - # TODO(mrsmith): add more asserts - - @mock.patch.object(ip_lib, 'IPWrapper') - @mock.patch.object(ip_lib, 'IPDevice') - @mock.patch.object(ip_lib, 'IPRule') - def test_floating_ip_removed_dist(self, mIPRule, mIPDevice, mIPWrapper): - router = mock.MagicMock() - ri = self._create_router(router) - - subnet_id = _uuid() - agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30', - 'prefixlen': 24, - 'subnet_id': subnet_id}], - 'subnets': [{'id': subnet_id, - 'cidr': '20.0.0.0/24', - 'gateway_ip': '20.0.0.1'}], - 'id': _uuid(), - 'network_id': _uuid(), - 'mac_address': 'ca:fe:de:ad:be:ef'} - fip_cidr = '11.22.33.44/24' - - ri.dist_fip_count = 2 - ri.fip_ns = mock.Mock() - ri.fip_ns.get_name.return_value = 'fip_ns_name' - ri.floating_ips_dict['11.22.33.44'] = FIP_PRI - ri.fip_2_rtr = '11.22.33.42' - ri.rtr_2_fip = '11.22.33.40' - ri.fip_ns.agent_gateway_port = agent_gw_port - s = lla.LinkLocalAddressPair('169.254.30.42/31') - ri.rtr_fip_subnet = s - ri.floating_ip_removed_dist(fip_cidr) - mIPRule().rule.delete.assert_called_with( - str(netaddr.IPNetwork(fip_cidr).ip), 16, FIP_PRI) - mIPDevice().route.delete_route.assert_called_with(fip_cidr, str(s.ip)) - self.assertFalse(ri.fip_ns.unsubscribe.called) - - ri.dist_fip_count = 1 - ri.rtr_fip_subnet = lla.LinkLocalAddressPair('15.1.2.3/32') - _, fip_to_rtr = ri.rtr_fip_subnet.get_pair() - fip_ns = ri.fip_ns - - ri.floating_ip_removed_dist(fip_cidr) - - self.assertTrue(fip_ns.destroyed) - mIPWrapper().del_veth.assert_called_once_with( - fip_ns.get_int_device_name(router['id'])) - mIPDevice().route.delete_gateway.assert_called_once_with( - str(fip_to_rtr.ip), table=16) - fip_ns.unsubscribe.assert_called_once_with(ri.router_id) - - def _test_add_floating_ip(self, ri, fip, is_failure): - ri._add_fip_addr_to_device = mock.Mock(return_value=is_failure) - ri.floating_ip_added_dist = mock.Mock() - - result = ri.add_floating_ip(fip, - mock.sentinel.interface_name, - mock.sentinel.device) - ri._add_fip_addr_to_device.assert_called_once_with( - fip, mock.sentinel.device) - return result - - def test_add_floating_ip(self): - ri = self._create_router(mock.MagicMock()) - ip = '15.1.2.3' - fip = {'floating_ip_address': ip} - result = self._test_add_floating_ip(ri, fip, True) - ri.floating_ip_added_dist.assert_called_once_with(fip, ip + '/32') - self.assertEqual(l3_constants.FLOATINGIP_STATUS_ACTIVE, result) - - def test_add_floating_ip_error(self): - ri = self._create_router(mock.MagicMock()) - result = self._test_add_floating_ip( - ri, {'floating_ip_address': '15.1.2.3'}, False) - self.assertFalse(ri.floating_ip_added_dist.called) - self.assertEqual(l3_constants.FLOATINGIP_STATUS_ERROR, result) - - @mock.patch.object(router_info.RouterInfo, 'remove_floating_ip') - def test_remove_floating_ip(self, super_remove_floating_ip): - ri = self._create_router(mock.MagicMock()) - ri.floating_ip_removed_dist = mock.Mock() - - ri.remove_floating_ip(mock.sentinel.device, mock.sentinel.ip_cidr) - - super_remove_floating_ip.assert_called_once_with( - mock.sentinel.device, mock.sentinel.ip_cidr) - ri.floating_ip_removed_dist.assert_called_once_with( - mock.sentinel.ip_cidr) - - def test__get_internal_port(self): - ri = self._create_router() - port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} - router_ports = [port] - ri.router.get.return_value = router_ports - self.assertEqual(port, ri._get_internal_port(mock.sentinel.subnet_id)) - - def test__get_internal_port_not_found(self): - ri = self._create_router() - port = {'fixed_ips': [{'subnet_id': mock.sentinel.subnet_id}]} - router_ports = [port] - ri.router.get.return_value = router_ports - self.assertEqual(None, ri._get_internal_port(mock.sentinel.subnet_id2)) - - def test__get_snat_idx_ipv4(self): - ip_cidr = '101.12.13.00/24' - ri = self._create_router(mock.MagicMock()) - snat_idx = ri._get_snat_idx(ip_cidr) - # 0x650C0D00 is numerical value of 101.12.13.00 - self.assertEqual(0x650C0D00, snat_idx) - - def test__get_snat_idx_ipv6(self): - ip_cidr = '2620:0:a03:e100::/64' - ri = self._create_router(mock.MagicMock()) - snat_idx = ri._get_snat_idx(ip_cidr) - # 0x3D345705 is 30 bit xor folded crc32 of the ip_cidr - self.assertEqual(0x3D345705, snat_idx) - - def test__get_snat_idx_ipv6_below_32768(self): - ip_cidr = 'd488::/30' - # crc32 of this ip_cidr is 0x1BD7 - ri = self._create_router(mock.MagicMock()) - snat_idx = ri._get_snat_idx(ip_cidr) - # 0x1BD7 + 0x3FFFFFFF = 0x40001BD6 - self.assertEqual(0x40001BD6, snat_idx) -- 2.45.2