[Agent Side] L3 router support ndp proxy

The agent side codes need consider three scenarios:
1. Non-dvr router. The all related rules are applied in
   qrouter-namespace
2. Dvr router with the local agent mode is dvr_no_external.
   The all related rules are applied in snat-namespace.
3. Dvr router with the local agent mode is dvr. In this scenario,
   The all related rules are applied in fip-namespace.

Change-Id: Ie8729586d318be4a673858021a0116e09e193522
Partial-Bug: #1877301
This commit is contained in:
Yang JianFeng 2020-08-05 00:39:33 +00:00 committed by yangjianfeng
parent 999bb965f7
commit 9b27020a65
9 changed files with 1557 additions and 20 deletions

View File

@ -89,6 +89,14 @@ class FipNamespace(namespaces.Namespace):
def get_rtr_ext_device_name(self, router_id):
return (ROUTER_2_FIP_DEV_PREFIX + router_id)[:self.driver.DEV_NAME_LEN]
def get_fip_2_rtr_device(self, ri):
fip_2_rtr_name = self.get_int_device_name(ri.router_id)
return ip_lib.IPDevice(fip_2_rtr_name, ri.fip_ns.name)
def get_rtr_2_fip_device(self, ri):
rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id)
return ip_lib.IPDevice(rtr_2_fip_name, ri.ns_name)
def has_subscribers(self):
return len(self._subscribers) != 0
@ -410,22 +418,19 @@ class FipNamespace(namespaces.Namespace):
def create_rtr_2_fip_link(self, ri):
"""Create interface between router and Floating IP namespace."""
LOG.debug("Create FIP link interfaces for router %s", ri.router_id)
rtr_2_fip_name = self.get_rtr_ext_device_name(ri.router_id)
fip_2_rtr_name = self.get_int_device_name(ri.router_id)
fip_ns_name = self.get_name()
# add link local IP to interface
if ri.rtr_fip_subnet is None:
ri.rtr_fip_subnet = self.local_subnets.allocate(ri.router_id)
rtr_2_fip, fip_2_rtr = ri.rtr_fip_subnet.get_pair()
rtr_2_fip_dev = ip_lib.IPDevice(rtr_2_fip_name, namespace=ri.ns_name)
fip_2_rtr_dev = ip_lib.IPDevice(fip_2_rtr_name, namespace=fip_ns_name)
rtr_2_fip_dev = self.get_rtr_2_fip_device(ri)
fip_2_rtr_dev = self.get_fip_2_rtr_device(ri)
if not rtr_2_fip_dev.exists():
ip_wrapper = ip_lib.IPWrapper(namespace=ri.ns_name)
rtr_2_fip_dev, fip_2_rtr_dev = ip_wrapper.add_veth(rtr_2_fip_name,
fip_2_rtr_name,
fip_ns_name)
rtr_2_fip_dev, fip_2_rtr_dev = ip_wrapper.add_veth(
rtr_2_fip_dev.name, fip_2_rtr_dev.name, fip_ns_name)
rtr_2_fip_dev.link.set_up()
fip_2_rtr_dev.link.set_up()
@ -444,10 +449,13 @@ class FipNamespace(namespaces.Namespace):
rtr_2_fip_dev.link.address)
self._add_rtr_ext_route_rule_to_route_table(ri, fip_2_rtr,
fip_2_rtr_name)
fip_2_rtr_dev.name)
# add default route for the link local interface
rtr_2_fip_dev.route.add_gateway(str(fip_2_rtr.ip), table=FIP_RT_TBL)
v6_gateway = common_utils.cidr_to_ip(
ip_lib.get_ipv6_lladdr(fip_2_rtr_dev.link.address))
rtr_2_fip_dev.route.add_gateway(v6_gateway)
def scan_fip_ports(self, ri):
# scan system for any existing fip ports

View File

@ -0,0 +1,451 @@
# Copyright 2021 Troila
# All Rights Reserved.
#
# 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 collections
import netaddr
from neutron_lib.agent import l3_extension
from neutron_lib import constants
from neutron_lib import rpc as n_rpc
from oslo_concurrency import lockutils
from oslo_log import log as logging
from neutron.agent.linux import ip_lib
from neutron.api.rpc.callbacks.consumer import registry
from neutron.api.rpc.callbacks import events
from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import resources_rpc
from neutron.common import coordination
from neutron.common import utils
LOG = logging.getLogger(__name__)
DEFAULT_NDP_PROXY_CHAIN = 'NDP'
class RouterNDPProxyMapping(object):
def __init__(self):
self.managed_ndp_proxies = {}
self.router_ndp_proxy_mapping = collections.defaultdict(set)
@lockutils.synchronized('ndp-proxy-cache')
def set_ndp_proxies(self, ndp_proxies):
for ndp_proxy in ndp_proxies:
self.router_ndp_proxy_mapping[
ndp_proxy.router_id].add(ndp_proxy.id)
self.managed_ndp_proxies[ndp_proxy.id] = ndp_proxy
@lockutils.synchronized('ndp-proxy-cache')
def get_ndp_proxy(self, ndp_proxy_id):
return self.managed_ndp_proxies.get(ndp_proxy_id)
@lockutils.synchronized('ndp-proxy-cache')
def del_ndp_proxies(self, ndp_proxies):
for ndp_proxy in ndp_proxies:
if not self.managed_ndp_proxies.get(ndp_proxy.id):
continue
del self.managed_ndp_proxies[ndp_proxy.id]
self.router_ndp_proxy_mapping[
ndp_proxy.router_id].remove(ndp_proxy.id)
if not self.router_ndp_proxy_mapping[ndp_proxy.router_id]:
del self.router_ndp_proxy_mapping[ndp_proxy.router_id]
@lockutils.synchronized('ndp-proxy-cache')
def get_ndp_proxies_by_router_id(self, router_id):
ndp_proxies = []
router_ndp_proxy_ids = self.router_ndp_proxy_mapping.get(router_id, [])
for ndp_proxy_id in router_ndp_proxy_ids:
ndp_proxies.append(self.managed_ndp_proxies.get(ndp_proxy_id))
return ndp_proxies
@lockutils.synchronized('ndp-proxy-cache')
def clear_by_router_id(self, router_id):
router_ndp_proxy_ids = self.router_ndp_proxy_mapping.get(router_id)
if not router_ndp_proxy_ids:
return
for ndp_proxy_id in router_ndp_proxy_ids:
del self.managed_ndp_proxies[ndp_proxy_id]
del self.router_ndp_proxy_mapping[router_id]
class NDPProxyAgentExtension(l3_extension.L3AgentExtension):
def initialize(self, connection, driver_type):
self.resource_rpc = resources_rpc.ResourcesPullRpcApi()
self._register_rpc_consumers()
self.mapping = RouterNDPProxyMapping()
def consume_api(self, agent_api):
self.agent_api = agent_api
def _register_rpc_consumers(self):
registry.register(self._handle_notification, resources.NDPPROXY)
self._connection = n_rpc.Connection()
endpoints = [resources_rpc.ResourcesPushRpcCallback()]
topic = resources_rpc.resource_type_versioned_topic(
resources.NDPPROXY)
self._connection.create_consumer(topic, endpoints, fanout=True)
self._connection.consume_in_threads()
def _handle_notification(self, context, resource_type,
ndp_proxies, event_type):
for ndp_proxy in ndp_proxies:
ri = self.agent_api.get_router_info(
ndp_proxy.router_id)
if not (ri and self._check_if_ri_need_process(ri) and
self._check_if_ndp_proxy_need_process(
context, ri, ndp_proxy)):
continue
(interface_name, namespace,
iptables_manager) = self._get_resource_by_router(ri)
agent_mode = ri.agent_conf.agent_mode
is_distributed = ri.router.get('distributed')
if (is_distributed and
agent_mode != constants.L3_AGENT_MODE_DVR_SNAT):
rtr_2_fip_dev = ri.fip_ns.get_rtr_2_fip_device(ri)
fip_2_rtr_dev = ri.fip_ns.get_fip_2_rtr_device(ri)
if event_type == events.CREATED:
LOG.debug("Create ndp proxy: %s.", ndp_proxy)
if (is_distributed and
agent_mode != constants.L3_AGENT_MODE_DVR_SNAT):
self._process_create_dvr([ndp_proxy], rtr_2_fip_dev,
fip_2_rtr_dev, interface_name,
namespace)
else:
self._process_create([ndp_proxy], interface_name,
namespace, iptables_manager)
self.mapping.set_ndp_proxies([ndp_proxy])
elif event_type == events.DELETED:
LOG.debug("Delete ndp proxy: %s.", ndp_proxy)
if (is_distributed and
agent_mode != constants.L3_AGENT_MODE_DVR_SNAT):
self._process_delete_dvr([ndp_proxy], rtr_2_fip_dev,
fip_2_rtr_dev, interface_name,
namespace)
else:
self._process_delete([ndp_proxy], interface_name,
namespace, iptables_manager)
self.mapping.del_ndp_proxies([ndp_proxy])
if iptables_manager:
iptables_manager.apply()
def _check_if_ri_need_process(self, ri):
if not (ri and ri.get_ex_gw_port()):
return False
is_distributed = ri.router.get('distributed')
agent_mode = ri.agent_conf.agent_mode
if (is_distributed and
agent_mode == constants.L3_AGENT_MODE_DVR_NO_EXTERNAL):
return False
if is_distributed and agent_mode == constants.L3_AGENT_MODE_DVR_SNAT:
if ri.router.get('gw_port_host') != ri.agent_conf.host:
return False
return True
def _check_if_ndp_proxy_need_process(self, context, ri, ndp_proxy):
"""Check the ndp proxy whether need processed by local l3 agent"""
agent_mode = ri.agent_conf.agent_mode
is_distributed = ri.router.get('distributed')
if not is_distributed:
return True
# dvr_no_external agent don't need process dvr router's ndp proxy
if agent_mode == constants.L3_AGENT_MODE_DVR_NO_EXTERNAL:
return False
port_obj = self.resource_rpc.bulk_pull(
context, resources.PORT, filter_kwargs={
'id': ndp_proxy['port_id']})[0]
if len(port_obj.bindings) != 1:
return False
if agent_mode == constants.L3_AGENT_MODE_DVR:
if port_obj.bindings[0].host == ri.agent_conf.host:
return True
# If the l3 agent mode is dvr_no_external of the host which the ndp
# proxy's port binding to, the rules related the ndp proxy should be
# applied in snat-namespace
if agent_mode == constants.L3_AGENT_MODE_DVR_SNAT:
agent_obj = self.resource_rpc.bulk_pull(
context, resources.AGENT,
filter_kwargs={
'host': port_obj.bindings[0].host,
'agent_type': constants.AGENT_TYPE_L3})[0]
if agent_obj.configurations['agent_mode'] == \
constants.L3_AGENT_MODE_DVR_NO_EXTERNAL:
return True
return False
def _get_resource_by_router(self, ri):
is_distributed = ri.router.get('distributed')
ex_gw_port = ri.get_ex_gw_port()
if not is_distributed:
interface_name = ri.get_external_device_interface_name(ex_gw_port)
namespace = ri.ns_name
iptables_manager = ri.iptables_manager
elif ri.agent_conf.agent_mode == constants.L3_AGENT_MODE_DVR_SNAT:
interface_name = ri.get_snat_external_device_interface_name(
ex_gw_port)
namespace = ri.snat_namespace.name
iptables_manager = ri.snat_iptables_manager
else:
interface_name = ri.fip_ns.get_ext_device_name(
ri.fip_ns.agent_gateway_port['id'])
namespace = ri.fip_ns.name
iptables_manager = None
return interface_name, namespace, iptables_manager
def _get_device_ipv6_lladdr(self, device):
lladdr_cidr = ip_lib.get_ipv6_lladdr(device.link.address)
return utils.cidr_to_ip(lladdr_cidr)
@coordination.synchronized('router-lock-ns-{namespace}')
def _process_create(self, ndp_proxies, interface_name,
namespace, iptables_manager):
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
for proxy in ndp_proxies:
v6_address = str(proxy.ip_address)
cmd = ['ip', '-6', 'neigh', 'add',
'proxy', v6_address, 'dev', interface_name]
ip_wrapper.netns.execute(cmd, privsep_exec=True)
accept_rule = '-i %s --destination %s -j ACCEPT' % (
interface_name, v6_address)
iptables_manager.ipv6['filter'].add_rule(
DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)
cmd = ['ndsend', v6_address, interface_name]
ip_wrapper.netns.execute(cmd, check_exit_code=False,
log_fail_as_error=True,
privsep_exec=True)
@coordination.synchronized('router-lock-ns-{namespace}')
def _process_create_dvr(self, ndp_proxies, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name, namespace):
for proxy in ndp_proxies:
rtr_2_fip_v6_address = self._get_device_ipv6_lladdr(rtr_2_fip_dev)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
v6_address = str(proxy.ip_address)
fip_2_rtr_dev.route.add_route(v6_address, via=rtr_2_fip_v6_address)
cmd = ['ip', '-6', 'neigh', 'add',
'proxy', v6_address, 'dev', interface_name]
ip_wrapper.netns.execute(cmd, privsep_exec=True)
cmd = ['ndsend', v6_address, interface_name]
ip_wrapper.netns.execute(cmd, check_exit_code=False,
log_fail_as_error=True,
privsep_exec=True)
@coordination.synchronized('router-lock-ns-{namespace}')
def _process_delete(self, ndp_proxies, interface_name,
namespace, iptables_manager):
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
for proxy in ndp_proxies:
v6_address = str(proxy.ip_address)
cmd = ['ip', '-6', 'neigh', 'del',
'proxy', v6_address, 'dev', interface_name]
ip_wrapper.netns.execute(cmd, privsep_exec=True)
accept_rule = '-i %s --destination %s -j ACCEPT' % (
interface_name, v6_address)
iptables_manager.ipv6['filter'].remove_rule(
DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)
@coordination.synchronized('router-lock-ns-{namespace}')
def _process_delete_dvr(self, ndp_proxies, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name, namespace):
rtr_2_fip_v6_address = self._get_device_ipv6_lladdr(rtr_2_fip_dev)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
for proxy in ndp_proxies:
v6_address = str(proxy.ip_address)
fip_2_rtr_dev.route.delete_route(
v6_address, via=rtr_2_fip_v6_address)
cmd = ['ip', '-6', 'neigh', 'del',
'proxy', v6_address, 'dev', interface_name]
ip_wrapper.netns.execute(cmd, privsep_exec=True)
def _get_router_info(self, router_id):
ri = self.agent_api.get_router_info(router_id)
if ri:
return ri
LOG.debug("Router %s is not managed by this agent. "
"It was possibly deleted concurrently.", router_id)
def _check_if_address_scopes_match(self, int_port, ex_gw_port):
"""Checks and returns the matching state for v6 scopes."""
int_port_addr_scopes = int_port.get('address_scopes', {})
ext_port_addr_scopes = ex_gw_port.get('address_scopes', {})
key = str(constants.IP_VERSION_6)
if int_port_addr_scopes.get(key) == ext_port_addr_scopes.get(key):
return True
return False
@coordination.synchronized('router-lock-ns-{namespace}')
def _init_ndp_proxy_rule(self, ri, interface_name,
iptables_manager, is_distributed, ip_wrapper,
namespace):
agent_mode = ri.agent_conf.agent_mode
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % interface_name]
dvr_with_snat = (
is_distributed and agent_mode == constants.L3_AGENT_MODE_DVR_SNAT)
if not is_distributed or dvr_with_snat:
# We need apply some iptable rules in centralized router namespace.
wrap_name = iptables_manager.wrap_name
existing_chains = iptables_manager.ipv6['filter'].chains
if DEFAULT_NDP_PROXY_CHAIN not in existing_chains:
iptables_manager.ipv6['filter'].add_chain(
DEFAULT_NDP_PROXY_CHAIN)
default_rule = '-i %s -j DROP' % interface_name
iptables_manager.ipv6['filter'].add_rule(
DEFAULT_NDP_PROXY_CHAIN, default_rule)
iptables_manager.apply()
new_subnet_cidrs = []
for internal_port in ri.internal_ports:
if self._check_if_address_scopes_match(
internal_port, ri.ex_gw_port):
for subnet in internal_port['subnets']:
if netaddr.IPNetwork(subnet['cidr']).version == \
constants.IP_VERSION_4:
continue
new_subnet_cidrs.append(subnet['cidr'])
existing_subnet_cidrs = []
for rule in iptables_manager.ipv6['filter'].rules:
if ("-j %s-%s") % (
wrap_name, DEFAULT_NDP_PROXY_CHAIN) not in rule.rule:
continue
rule_lists = rule.rule.split(' ')
for item in rule_lists:
try:
netaddr.IPNetwork(item)
except netaddr.core.AddrFormatError:
continue
existing_subnet_cidrs.append(item)
need_add = set(new_subnet_cidrs) - set(existing_subnet_cidrs)
need_del = set(existing_subnet_cidrs) - set(new_subnet_cidrs)
for cidr in need_add:
subnet_rule = (
'-i %s --destination %s -j '
'%s-%s') % (interface_name, cidr,
wrap_name, DEFAULT_NDP_PROXY_CHAIN)
iptables_manager.ipv6['filter'].add_rule(
'FORWARD', subnet_rule)
for cidr in need_del:
subnet_rule = (
'-i %s --destination %s -j '
'%s-%s') % (interface_name, cidr,
wrap_name, DEFAULT_NDP_PROXY_CHAIN)
iptables_manager.ipv6['filter'].remove_rule(
'FORWARD', subnet_rule)
ip_wrapper.netns.execute(sysctl_cmd, privsep_exec=True)
def _process_router(self, context, data):
state = data.get('enable_ndp_proxy', False)
ri = self._get_router_info(data['id'])
if not self._check_if_ri_need_process(ri):
return
agent_mode = ri.agent_conf.agent_mode
(interface_name, namespace,
iptables_manager) = self._get_resource_by_router(ri)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
is_distributed = ri.router.get('distributed')
if is_distributed and agent_mode != constants.L3_AGENT_MODE_DVR_SNAT:
rtr_2_fip_dev = ri.fip_ns.get_rtr_2_fip_device(ri)
fip_2_rtr_dev = ri.fip_ns.get_fip_2_rtr_device(ri)
existing_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
ri.router_id)
if state:
self._init_ndp_proxy_rule(
ri, interface_name, iptables_manager,
is_distributed, ip_wrapper, namespace)
ndp_proxies = self.resource_rpc.bulk_pull(
context, resources.NDPPROXY,
filter_kwargs={'router_id': [data['id']]})
need_create = set(ndp_proxies) - set(existing_ndp_proxies)
need_delete = set(existing_ndp_proxies) - set(ndp_proxies)
def filter_ndp_proxies(ri, ndp_proxies):
result = []
for ndp_proxy in ndp_proxies:
if self._check_if_ndp_proxy_need_process(
context, ri, ndp_proxy):
result.append(ndp_proxy)
return result
need_create = filter_ndp_proxies(ri, need_create)
if is_distributed and agent_mode == constants.L3_AGENT_MODE_DVR:
self._process_create_dvr(need_create, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name,
namespace)
self._process_delete_dvr(need_delete, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name,
namespace)
else:
self._process_create(need_create, interface_name,
namespace, iptables_manager)
self._process_delete(need_delete, interface_name,
namespace, iptables_manager)
self.mapping.set_ndp_proxies(need_create)
else:
if is_distributed and agent_mode == constants.L3_AGENT_MODE_DVR:
self._process_delete_dvr(
existing_ndp_proxies, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name, namespace)
else:
self._clear_ndp_proxies(
ip_wrapper, iptables_manager,
interface_name, namespace)
self.mapping.clear_by_router_id(ri.router_id)
if iptables_manager:
iptables_manager.apply()
@coordination.synchronized('router-lock-ns-{namespace}')
def _clear_ndp_proxies(self, ip_wrapper, iptables_manager,
interface_name, namespace):
cmd = ['ip', '-6', 'neigh', 'flush', 'proxy']
ip_wrapper.netns.execute(cmd, check_exit_code=False,
privsep_exec=True)
iptables_manager.ipv6['filter'].remove_chain(
DEFAULT_NDP_PROXY_CHAIN)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=0' % interface_name]
ip_wrapper.netns.execute(sysctl_cmd, privsep_exec=True)
def add_router(self, context, data):
self._process_router(context, data)
def update_router(self, context, data):
self._process_router(context, data)
def delete_router(self, context, data):
# Just process dvr router, clear the fip-namespace related rules
if not data['distributed']:
return
ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(data['id'])
if not ndp_proxies:
return
ri = self._get_router_info(data['id'])
(interface_name, namespace,
iptables_manager) = self._get_resource_by_router(ri)
rtr_2_fip_dev = ri.fip_ns.get_rtr_2_fip_device(ri)
fip_2_rtr_dev = ri.fip_ns.get_fip_2_rtr_device(ri)
self._process_delete_dvr(ndp_proxies, rtr_2_fip_dev,
fip_2_rtr_dev, interface_name, namespace)
def ha_state_change(self, context, data):
if data['state'] == 'backup':
return
self._process_router(context, data)
def update_network(self, context, data):
pass

View File

@ -12,9 +12,11 @@
from neutron._i18n import _
from neutron.objects import address_group
from neutron.objects import agent
from neutron.objects import conntrack_helper
from neutron.objects import local_ip
from neutron.objects.logapi import logging_resource as log_object
from neutron.objects import ndp_proxy
from neutron.objects import network
from neutron.objects import port_forwarding
from neutron.objects import ports
@ -38,6 +40,8 @@ PORTFORWARDING = port_forwarding.PortForwarding.obj_name()
CONNTRACKHELPER = conntrack_helper.ConntrackHelper.obj_name()
ADDRESSGROUP = address_group.AddressGroup.obj_name()
LOCAL_IP_ASSOCIATION = local_ip.LocalIPAssociation.obj_name()
NDPPROXY = ndp_proxy.NDPProxy.obj_name()
AGENT = agent.Agent.obj_name()
_VALID_CLS = (
@ -54,6 +58,8 @@ _VALID_CLS = (
conntrack_helper.ConntrackHelper,
address_group.AddressGroup,
local_ip.LocalIPAssociation,
ndp_proxy.NDPProxy,
agent.Agent,
)
_TYPE_TO_CLS_MAP = {cls.obj_name(): cls for cls in _VALID_CLS}

View File

@ -0,0 +1,310 @@
# Copyright 2021 Troila
# All Rights Reserved.
#
# 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 unittest import mock
import netaddr
from neutron_lib import constants
from oslo_utils import uuidutils
from neutron.agent.l3 import agent as neutron_l3_agent
from neutron.agent.l3.extensions import ndp_proxy as np
from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager as iptable_mng
from neutron.api.rpc.callbacks import resources
from neutron.common import utils as common_utils
from neutron.objects import agent as agent_obj
from neutron.objects import ndp_proxy as np_obj
from neutron.objects import ports as ports_obj
from neutron.tests.functional.agent.l3 import framework
from neutron.tests.functional.agent.l3 import test_dvr_router
HOSTNAME = 'agent1'
class L3AgentNDPProxyTestFramework(framework.L3AgentTestFramework):
def setUp(self):
super(L3AgentNDPProxyTestFramework, self).setUp()
self.conf.set_override('extensions', ['ndp_proxy'], 'agent')
self.agent = neutron_l3_agent.L3NATAgentWithStateReport(HOSTNAME,
self.conf)
self.np_ext = np.NDPProxyAgentExtension()
port_id1 = uuidutils.generate_uuid()
port1_binding = ports_obj.PortBinding(port_id=port_id1,
host=self.agent.host)
port1_obj = ports_obj.Port(id=port_id1, bindings=[port1_binding])
port_id2 = uuidutils.generate_uuid()
port2_binding = ports_obj.PortBinding(port_id=port_id1,
host='fake_host')
port2_obj = ports_obj.Port(id=port_id2, bindings=[port2_binding])
self.ports = [port1_obj, port2_obj]
self.port_binding_map = {port_id1: port1_binding,
port_id2: port2_binding}
self.ndpproxy1 = np_obj.NDPProxy(
context=None, id=uuidutils.generate_uuid(),
router_id=uuidutils.generate_uuid(),
port_id=port_id1, ip_address='2002::1:3')
self.ndpproxy2 = np_obj.NDPProxy(
context=None, id=uuidutils.generate_uuid(),
router_id=uuidutils.generate_uuid(),
port_id=port_id2, ip_address='2002::1:4')
self.ndp_proxies = [self.ndpproxy1, self.ndpproxy2]
agent_configurations = {
'agent_mode': constants.L3_AGENT_MODE_DVR_NO_EXTERNAL}
self.agent_obj = agent_obj.Agent(
id=uuidutils.generate_uuid(), host=self.agent.host,
agent_type=constants.AGENT_TYPE_L3,
configurations=agent_configurations)
self._set_pull_mock()
def _set_pull_mock(self):
def _bulk_pull_mock(context, resource_type, filter_kwargs=None):
if resource_type == resources.PORT:
return [port for port in self.ports if
port.id == filter_kwargs['id']]
if resource_type == resources.AGENT:
return [self.agent_obj]
if resource_type == resources.NDPPROXY:
result = []
if 'router_id' in filter_kwargs:
for ndp_proxy in self.ndp_proxies:
if ndp_proxy.router_id in filter_kwargs['router_id']:
result.append(ndp_proxy)
return result
return self.ndp_proxie
self.pull = mock.patch('neutron.api.rpc.handlers.resources_rpc.'
'ResourcesPullRpcApi.pull').start()
self.bulk_pull = mock.patch('neutron.api.rpc.handlers.resources_rpc.'
'ResourcesPullRpcApi.bulk_pull').start()
self.bulk_pull.side_effect = _bulk_pull_mock
def _get_existing_ndp_proxies(self, interface_name, namespace):
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
cmd = ['ip', '-6', 'neigh', 'list', 'proxy']
res = ip_wrapper.netns.execute(cmd)
proxies = []
for proxy in res.split('\n'):
# Exclute null line
if proxy:
proxy_list = proxy.split(' ')
if interface_name in proxy_list:
try:
if netaddr.IPAddress(proxy_list[0]).version == 6:
proxies.append(proxy_list[0])
except Exception:
pass
return proxies
def _assert_ndp_proxy_kernel_parameter(self, ip_wrapper, interface_name):
sysctl_cmd = ['sysctl', '-b',
'net.ipv6.conf.%s.proxy_ndp' % interface_name]
def check_kernel_parameter():
res = ip_wrapper.netns.execute(sysctl_cmd, privsep_exec=True)
if res == "1":
return True
common_utils.wait_until_true(check_kernel_parameter)
def _assert_ndp_iptable_chain_is_set(self, iptables_manager,
interface_name):
rule = '-i %s -j DROP' % interface_name
rule_obj = iptable_mng.IptablesRule('NDP', rule, True, False,
iptables_manager.wrap_name)
def check_chain_is_set():
existing_chains = iptables_manager.ipv6['filter'].chains
if 'NDP' not in existing_chains:
return False
existing_rules = iptables_manager.ipv6['filter'].rules
if rule_obj in existing_rules:
return True
common_utils.wait_until_true(check_chain_is_set)
def _assert_ndp_proxy_state_iptable_rules_is_set(
self, ri, iptables_manager, interface_name):
wrap_name = iptables_manager.wrap_name
expected_rules = []
for port in ri.internal_ports:
for subnet in port['subnets']:
if netaddr.IPNetwork(subnet['cidr']).version == \
constants.IP_VERSION_4:
continue
rule = (
'-i %s --destination %s -j '
'%s-NDP') % (interface_name,
subnet['cidr'],
wrap_name)
rule_obj = iptable_mng.IptablesRule(
'FORWARD', rule, True, False, iptables_manager.wrap_name)
expected_rules.append(rule_obj)
def check_rules_is_set():
existing_rules = iptables_manager.ipv6['filter'].rules
for rule in expected_rules:
if rule not in existing_rules:
return False
return True
common_utils.wait_until_true(check_rules_is_set)
def _assect_ndp_proxy_rules_is_set(self, ip_wrapper, iptables_manager,
interface_name, namespace):
expected_iptable_rules = []
expected_proxy_address = []
for ndp_proxy in self.ndp_proxies:
rule = '-i %s --destination %s -j ACCEPT' % (interface_name,
ndp_proxy.ip_address)
rule_obj = iptable_mng.IptablesRule('NDP', rule, True, True,
iptables_manager.wrap_name)
expected_iptable_rules.append(rule_obj)
expected_proxy_address.append(str(ndp_proxy.ip_address))
def check_rules_is_set():
existing_iptable_rules = iptables_manager.ipv6['filter'].rules
for iptable_rule in expected_iptable_rules:
if iptable_rule not in existing_iptable_rules:
return False
existing_proxy_addresses = self._get_existing_ndp_proxies(
interface_name, namespace)
for address in expected_proxy_address:
if address not in existing_proxy_addresses:
return False
return True
common_utils.wait_until_true(check_rules_is_set)
class TestL3AgentNDPProxyExtension(L3AgentNDPProxyTestFramework):
def _test_router_ndp_proxy(self, enable_ha):
router_info = self.generate_router_info(enable_ha=enable_ha)
router_info['enable_ndp_proxy'] = True
ri = self.manage_router(self.agent, router_info)
for ndp_proxy in self.ndp_proxies:
ndp_proxy.router_id = ri.router_id
(interface_name, namespace,
iptables_manager) = self.np_ext._get_resource_by_router(ri)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
self._assert_ndp_proxy_kernel_parameter(ip_wrapper, interface_name)
self._assert_ndp_iptable_chain_is_set(iptables_manager, interface_name)
slaac = constants.IPV6_SLAAC
slaac_mode = {'ra_mode': slaac, 'address_mode': slaac}
self._add_internal_interface_by_subnet(
ri.router, count=1, ip_version=constants.IP_VERSION_6,
ipv6_subnet_modes=[slaac_mode])
self.agent._process_updated_router(ri.router)
self._assert_ndp_proxy_state_iptable_rules_is_set(
ri, iptables_manager, interface_name)
self._assect_ndp_proxy_rules_is_set(
ip_wrapper, iptables_manager,
interface_name, namespace)
ri.router['enable_ndp_proxy'] = False
self.agent._process_updated_router(ri.router)
def test_legacy_router_ndp_proxy(self):
self._test_router_ndp_proxy(enable_ha=False)
def test_ha_router_ndp_proxy(self):
self._test_router_ndp_proxy(enable_ha=True)
class TestL3AgentNDPProxyExtensionDVR(test_dvr_router.TestDvrRouter,
L3AgentNDPProxyTestFramework):
def test_local_dvr_router(self):
self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR
router_info = self.generate_dvr_router_info(enable_ha=False)
for ndp_proxy in self.ndp_proxies:
ndp_proxy.router_id = router_info['id']
router_info['enable_ndp_proxy'] = True
ri = self.manage_router(self.agent, router_info)
(interface_name, namespace,
iptables_manager) = self.np_ext._get_resource_by_router(ri)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
self._assert_ndp_proxy_kernel_parameter(ip_wrapper, interface_name)
self._assect_ndp_proxy_rules_is_set(ri, interface_name, namespace)
def test_edge_dvr_router(self):
self.agent.conf.agent_mode = constants.L3_AGENT_MODE_DVR_SNAT
router_info = self.generate_dvr_router_info(enable_ha=False)
for ndp_proxy in self.ndp_proxies:
ndp_proxy.router_id = router_info['id']
router_info['enable_ndp_proxy'] = True
ri = self.manage_router(self.agent, router_info)
(interface_name, namespace,
iptables_manager) = self.np_ext._get_resource_by_router(ri)
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
self._assert_ndp_proxy_kernel_parameter(ip_wrapper, interface_name)
self._assert_ndp_iptable_chain_is_set(iptables_manager, interface_name)
slaac = constants.IPV6_SLAAC
slaac_mode = {'ra_mode': slaac, 'address_mode': slaac}
self._add_internal_interface_by_subnet(
ri.router, count=1, ip_version=constants.IP_VERSION_6,
ipv6_subnet_modes=[slaac_mode])
self.agent._process_updated_router(ri.router)
self._assert_ndp_proxy_state_iptable_rules_is_set(
ri, iptables_manager, interface_name)
super(
TestL3AgentNDPProxyExtensionDVR,
self)._assect_ndp_proxy_rules_is_set(
ip_wrapper, iptables_manager,
interface_name, namespace)
ri.router['enable_ndp_proxy'] = False
self.agent._process_updated_router(ri.router)
def _assect_ndp_proxy_rules_is_set(self, ri, interface_name, namespace):
rtr_2_fip_dev = ri.fip_ns.get_rtr_2_fip_device(ri)
fip_2_rtr_dev = ri.fip_ns.get_fip_2_rtr_device(ri)
rtr_2_fip_v6_address = self.np_ext._get_device_ipv6_lladdr(
rtr_2_fip_dev)
expected_proxy_address = []
expected_routes = []
for ndp_proxy in self.ndp_proxies:
port_binding_obj = self.port_binding_map.get(ndp_proxy.port_id)
if port_binding_obj and port_binding_obj.host == self.agent.host:
expected_proxy_address.append(str(ndp_proxy.ip_address))
expected_routes.append(
{'table': 'main', 'source_prefix': None,
'cidr': '%s/128' % ndp_proxy.ip_address,
'scope': 'global', 'metric': 1024,
'proto': 'static', 'device': fip_2_rtr_dev.name,
'via': rtr_2_fip_v6_address})
def check_rules_is_set():
existing_proxy_addresses = self._get_existing_ndp_proxies(
interface_name, namespace)
for address in expected_proxy_address:
if address not in existing_proxy_addresses:
return False
existing_routes = fip_2_rtr_dev.route.list_routes(
ip_version=constants.IP_VERSION_6)
for route in expected_routes:
if route not in existing_routes:
return False
return True
common_utils.wait_until_true(check_rules_is_set)

View File

@ -892,8 +892,11 @@ class TestDvrRouter(DvrRouterTestFramework, framework.L3AgentTestFramework):
rfp_device = ip_lib.IPDevice(rfp_device_name,
namespace=router1.ns_name)
rtr_2_fip, fip_2_rtr = router1.rtr_fip_subnet.get_pair()
fpr_device_name = router1.fip_ns.get_int_device_name(router1.router_id)
fpr_device = ip_lib.IPDevice(fpr_device_name,
namespace=fip_ns_name)
self._assert_default_gateway(
fip_2_rtr, rfp_device, rfp_device_name)
fip_2_rtr, rfp_device, rfp_device_name, fpr_device)
router1.router[lib_constants.FLOATINGIP_KEY] = []
self.agent._process_updated_router(router1.router)
@ -914,16 +917,26 @@ class TestDvrRouter(DvrRouterTestFramework, framework.L3AgentTestFramework):
self.assertEqual(2, interface_rules_list_count)
self.assertEqual(0, fip_rule_count)
def _assert_default_gateway(self, fip_2_rtr, rfp_device, device_name):
def _assert_default_gateway(self, fip_2_rtr, rfp_device,
device_name, fpr_device):
v6_gateway = utils.cidr_to_ip(
ip_lib.get_ipv6_lladdr(fpr_device.link.address))
expected_gateway = [{'device': device_name,
'cidr': '0.0.0.0/0',
'via': str(fip_2_rtr.ip),
'table': dvr_fip_ns.FIP_RT_TBL}]
listed_routes = rfp_device.route.list_routes(
'table': dvr_fip_ns.FIP_RT_TBL},
{'device': device_name,
'cidr': '::/0',
'table': 'main',
'via': v6_gateway}]
v4_routes = rfp_device.route.list_routes(
ip_version=lib_constants.IP_VERSION_4,
table=dvr_fip_ns.FIP_RT_TBL,
via=str(fip_2_rtr.ip))
self._check_routes(expected_gateway, listed_routes)
v6_routers = rfp_device.route.list_routes(
ip_version=lib_constants.IP_VERSION_6,
via=v6_gateway)
self._check_routes(expected_gateway, v4_routes + v6_routers)
def test_dvr_router_rem_fips_on_restarted_agent(self):
self.agent.conf.agent_mode = 'dvr_snat'
@ -1965,7 +1978,7 @@ class TestDvrRouter(DvrRouterTestFramework, framework.L3AgentTestFramework):
namespace=fip_ns_name)
rtr_2_fip, fip_2_rtr = router1.rtr_fip_subnet.get_pair()
self._assert_default_gateway(
fip_2_rtr, rfp_device, rfp_device_name)
fip_2_rtr, rfp_device, rfp_device_name, fpr_device)
# Check if any snat redirect rules in the router namespace exist.
ip4_rules_list = ip_lib.list_ip_rules(router1.ns_name,

View File

@ -0,0 +1,740 @@
# Copyright 2021 Troila
# All Rights Reserved.
#
# 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 unittest import mock
from neutron_lib import constants as lib_const
from neutron_lib import context
from oslo_utils import uuidutils
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.l3 import dvr_edge_router
from neutron.agent.l3 import dvr_local_router as dvr_router
from neutron.agent.l3.extensions import ndp_proxy as np
from neutron.agent.l3 import l3_agent_extension_api as l3_ext_api
from neutron.agent.l3 import router_info
from neutron.agent.linux import iptables_manager
from neutron.api.rpc.callbacks.consumer import registry
from neutron.api.rpc.callbacks import events
from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import resources_rpc
from neutron.objects import agent as agent_obj
from neutron.objects import ndp_proxy as np_obj
from neutron.objects import ports as ports_obj
from neutron.tests import base
from neutron.tests.unit.agent.l3 import test_agent
from neutron.tests.unit.agent.l3 import test_dvr_local_router
_uuid = uuidutils.generate_uuid
HOSTNAME = 'testhost'
class NDPProxyExtensionTestCaseBase(base.BaseTestCase):
def setUp(self):
super(NDPProxyExtensionTestCaseBase, self).setUp()
self.context = context.get_admin_context()
self.connection = mock.Mock()
self.ext_port_id = _uuid()
self.ex_net_id = _uuid()
self.ex_gw_port = {'id': self.ext_port_id,
'network_id': self.ex_net_id,
'gw_port_host': HOSTNAME}
self.fake_router_id = _uuid()
self.port_id = _uuid()
self.agent_api = l3_ext_api.L3AgentExtensionAPI(None, None)
self.np_ext = np.NDPProxyAgentExtension()
self.np_ext.consume_api(self.agent_api)
self.np_ext.initialize(
self.connection, lib_const.L3_AGENT_MODE)
self.ndpproxy = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.fake_router_id,
port_id=self.port_id, ip_address='2002::1:3')
port_binding = ports_obj.PortBinding(port_id=self.port_id,
host=HOSTNAME)
port_obj = ports_obj.Port(id=self.port_id, bindings=[port_binding])
self.ndp_proxies = [self.ndpproxy]
self.ports = [port_obj]
agent_configurations = {
'agent_mode': lib_const.L3_AGENT_MODE_DVR_NO_EXTERNAL}
self.agent_obj = agent_obj.Agent(
id=_uuid(), host=HOSTNAME,
agent_type=lib_const.AGENT_TYPE_L3,
configurations=agent_configurations)
self.ip_wrapper = mock.patch('neutron.agent.linux.'
'ip_lib.IPWrapper').start()
self._set_pull_mock()
def _set_pull_mock(self):
def _bulk_pull_mock(context, resource_type, filter_kwargs=None):
if resource_type == resources.PORT:
return [port for port in self.ports if
port.id == filter_kwargs['id']]
if resource_type == resources.AGENT:
return [self.agent_obj]
if resource_type == resources.NDPPROXY:
result = []
if 'router_id' in filter_kwargs:
for ndp_proxy in self.ndp_proxies:
if ndp_proxy.router_id in filter_kwargs['router_id']:
result.append(ndp_proxy)
return result
return self.ndp_proxie
self.pull = mock.patch('neutron.api.rpc.handlers.resources_rpc.'
'ResourcesPullRpcApi.pull').start()
self.bulk_pull = mock.patch('neutron.api.rpc.handlers.resources_rpc.'
'ResourcesPullRpcApi.bulk_pull').start()
self.bulk_pull.side_effect = _bulk_pull_mock
class NDPProxyExtensionDVRTestCase(
NDPProxyExtensionTestCaseBase,
test_dvr_local_router.TestDvrRouterOperations):
def setUp(self):
super(NDPProxyExtensionDVRTestCase, self).setUp()
self.conf.host = HOSTNAME
self.conf.agent_mode = lib_const.L3_AGENT_MODE_DVR
self.agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
self.add_route = mock.MagicMock()
self.delete_route = mock.MagicMock()
mock_route_cmd = mock.MagicMock()
mock_route_cmd.add_route = self.add_route
mock_route_cmd.delete_route = self.delete_route
self.mock_ip_dev.route = mock_route_cmd
self.lladdr = "fe80::f816:3eff:fe5f:9d67"
get_ipv6_lladdr = mock.patch("neutron.agent.linux.ip_lib."
"get_ipv6_lladdr").start()
get_ipv6_lladdr.return_value = "%s/64" % self.lladdr
self.router = {'id': self.fake_router_id,
'gw_port': self.ex_gw_port,
'ha': False,
'distributed': True,
'enable_ndp_proxy': True}
kwargs = {
'agent': self.agent,
'router_id': self.fake_router_id,
'router': self.router,
'agent_conf': self.conf,
'interface_driver': mock.Mock()}
self.router_info = dvr_router.DvrLocalRouter(HOSTNAME, **kwargs)
self.get_router_info = mock.patch(
'neutron.agent.l3.l3_agent_extension_api.'
'L3AgentExtensionAPI.get_router_info').start()
self.get_router_info.return_value = self.router_info
self.router_info.fip_ns = self.agent.get_fip_ns(self.ex_net_id)
agent_ext_port_id = _uuid()
self.router_info.fip_ns.agent_gateway_port = {'id': agent_ext_port_id}
self.namespace = "fip-%s" % self.ex_net_id
self.agent_ext_dvice = "fg-%s" % agent_ext_port_id[:11]
self.ip_wrapper.reset_mock()
def test_create_router(self):
self.np_ext.add_router(self.context, self.router)
expected_calls = [
mock.call('2002::1:3', via=self.lladdr)]
self.assertEqual(expected_calls, self.add_route.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.agent_ext_dvice]
proxy_cmd = ['ip', '-6', 'neigh', 'add',
'proxy', '2002::1:3', 'dev', self.agent_ext_dvice]
ndsend_cmd = ['ndsend', '2002::1:3', self.agent_ext_dvice]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(
ndsend_cmd, check_exit_code=False,
log_fail_as_error=True, privsep_exec=True),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def test_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_route.reset_mock()
self.ip_wrapper.reset_mock()
self.np_ext.update_router(self.context, self.router)
self.add_route.assert_not_called()
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.agent_ext_dvice]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def test_add_ndp_proxy_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_route.reset_mock()
self.ip_wrapper.reset_mock()
ndpproxy = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.fake_router_id,
port_id=self.port_id, ip_address='2002::1:6')
self.ndp_proxies.append(ndpproxy)
self.np_ext.update_router(self.context, self.router)
expected_calls = [
mock.call('2002::1:6', via=self.lladdr)]
self.assertEqual(expected_calls, self.add_route.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.agent_ext_dvice]
proxy_cmd = ['ip', '-6', 'neigh', 'add',
'proxy', '2002::1:6', 'dev', self.agent_ext_dvice]
ndsend_cmd = ['ndsend', '2002::1:6', self.agent_ext_dvice]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(
ndsend_cmd, check_exit_code=False,
log_fail_as_error=True, privsep_exec=True),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def test_del_ndp_proxy_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_route.reset_mock()
self.ip_wrapper.reset_mock()
self.ndp_proxies = []
self.np_ext.update_router(self.context, self.router)
self.add_route.assert_not_called()
expected_calls = [
mock.call('2002::1:3', via=self.lladdr)]
self.assertEqual(expected_calls, self.delete_route.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.agent_ext_dvice]
proxy_cmd = ['ip', '-6', 'neigh', 'del',
'proxy', '2002::1:3', 'dev', self.agent_ext_dvice]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def test__handle_notification(self):
self.np_ext.add_router(self.context, self.router)
self.add_route.reset_mock()
self.ip_wrapper.reset_mock()
ndpproxy = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.fake_router_id,
port_id=self.port_id, ip_address='2002::1:5')
self.np_ext._handle_notification(mock.MagicMock(), mock.MagicMock(),
[ndpproxy], events.CREATED)
expected_calls = [
mock.call('2002::1:5', via=self.lladdr)]
self.assertEqual(expected_calls, self.add_route.mock_calls)
proxy_cmd = ['ip', '-6', 'neigh', 'add',
'proxy', '2002::1:5', 'dev', self.agent_ext_dvice]
ndsend_cmd = ['ndsend', '2002::1:5', self.agent_ext_dvice]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(
ndsend_cmd, check_exit_code=False,
log_fail_as_error=True, privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
self.add_route.reset_mock()
self.ip_wrapper.reset_mock()
self.np_ext._handle_notification(mock.MagicMock(), mock.MagicMock(),
[ndpproxy], events.DELETED)
self.add_route.assert_not_called()
expected_calls = [
mock.call('2002::1:5', via=self.lladdr)]
self.assertEqual(expected_calls, self.delete_route.mock_calls)
proxy_cmd = ['ip', '-6', 'neigh', 'del',
'proxy', '2002::1:5', 'dev', self.agent_ext_dvice]
expceted_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True)]
self.assertEqual(expceted_calls, self.ip_wrapper.mock_calls)
class NDPProxyExtensionLegacyDVRNoExternalTestCaseBase(
NDPProxyExtensionTestCaseBase,
test_agent.BasicRouterOperationsFramework):
def _mock_iptables_actions(self):
self.get_router_info = mock.patch(
'neutron.agent.l3.l3_agent_extension_api.'
'L3AgentExtensionAPI.get_router_info').start()
self.add_chain = mock.patch('neutron.agent.linux.iptables_manager.'
'IptablesTable.add_chain').start()
self.remove_chain = mock.patch('neutron.agent.linux.iptables_manager.'
'IptablesTable.remove_chain').start()
self.add_rule = mock.patch('neutron.agent.linux.iptables_manager.'
'IptablesTable.add_rule').start()
self.remove_rule = mock.patch('neutron.agent.linux.iptables_manager.'
'IptablesTable.remove_rule').start()
def _add_chain_mock(chain):
self.iptables_manager.ipv6[
'filter'].chains.append(chain)
def _remove_chain_mock(chain):
self.iptables_manager.ipv6[
'filter'].chains.remove(chain)
def _add_rule_mock(chain, rule, top=False):
rule_obj = mock.MagicMock()
rule_obj.rule = rule
self.iptables_manager.ipv6[
'filter'].rules.append(rule_obj)
def _remove_rule_mock(chain, rule, top=False):
for rule_obj in self.iptables_manager.ipv6[
'filter'].rules:
if rule == rule_obj.rule:
self.iptables_manager.ipv6[
'filter'].rules.remove(rule_obj)
break
self.get_router_info.return_value = self.router_info
self.add_chain.side_effect = _add_chain_mock
self.remove_chain.side_effect = _remove_chain_mock
self.add_rule.side_effect = _add_rule_mock
self.remove_rule.side_effect = _remove_rule_mock
def _test_create_router(self):
self.np_ext.add_router(self.context, self.router)
expected_calls = [mock.call(np.DEFAULT_NDP_PROXY_CHAIN)]
self.assertEqual(expected_calls, self.add_chain.mock_calls)
default_rule = '-i %s -j DROP' % self.ext_device_name
subnet_rule = ('-i %s --destination %s -j %s-%s') % (
self.ext_device_name, '2001::1:0/112', self.wrap_name,
np.DEFAULT_NDP_PROXY_CHAIN)
accept_rule = '-i %s --destination %s -j ACCEPT' % (
self.ext_device_name, '2002::1:3')
expected_calls = [
mock.call(np.DEFAULT_NDP_PROXY_CHAIN, default_rule),
mock.call('FORWARD', subnet_rule),
mock.call(np.DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)]
self.assertEqual(expected_calls, self.add_rule.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
proxy_cmd = ['ip', '-6', 'neigh', 'add', 'proxy',
'2002::1:3', 'dev', self.ext_device_name]
ndsend_cmd = ['ndsend', '2002::1:3', self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(ndsend_cmd, check_exit_code=False,
log_fail_as_error=True,
privsep_exec=True),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
self.add_rule.assert_not_called()
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_add_ndp_proxy_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.ndp_proxies.append(
np_obj.NDPProxy(context=None, id=_uuid(),
router_id=self.fake_router_id,
port_id=self.port_id,
ip_address='2002::1:4'))
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
accept_rule = '-i %s --destination %s -j ACCEPT' % (
self.ext_device_name, '2002::1:4')
expected_calls = [
mock.call(np.DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)]
self.assertEqual(expected_calls, self.add_rule.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
proxy_cmd = ['ip', '-6', 'neigh', 'add', 'proxy',
'2002::1:4', 'dev', self.ext_device_name]
ndsend_cmd = ['ndsend', '2002::1:4', self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(ndsend_cmd, check_exit_code=False,
log_fail_as_error=True,
privsep_exec=True),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_del_ndp_proxy_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.ndp_proxies = []
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
self.remove_chain.assert_not_called()
self.add_rule.assert_not_called()
accept_rule = '-i %s --destination %s -j ACCEPT' % (
self.ext_device_name, '2002::1:3')
expected_calls = [mock.call(np.DEFAULT_NDP_PROXY_CHAIN,
accept_rule, top=True)]
self.assertEqual(expected_calls, self.remove_rule.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
proxy_cmd = ['ip', '-6', 'neigh', 'del',
'proxy', '2002::1:3', 'dev', self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_add_subnet_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.internal_ports.append(
{'subnets': [{'cidr': '2001::5:0/112'}]})
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
self.remove_chain.assert_not_called()
subnet_rule = ('-i %s --destination 2001::5:0/112 -j %s-%s') % (
self.ext_device_name, self.wrap_name,
np.DEFAULT_NDP_PROXY_CHAIN)
expected_calls = [mock.call('FORWARD', subnet_rule)]
self.assertEqual(expected_calls, self.add_rule.mock_calls)
self.remove_rule.assert_not_called()
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_remove_subnet_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.router_info.internal_ports = []
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
self.remove_chain.assert_not_called()
self.add_rule.assert_not_called()
subnet_rule = ('-i %s --destination %s -j %s-%s') % (
self.ext_device_name, '2001::1:0/112', self.wrap_name,
np.DEFAULT_NDP_PROXY_CHAIN)
expected_calls = [mock.call('FORWARD', subnet_rule)]
self.assertEqual(expected_calls, self.remove_rule.mock_calls)
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=1' % self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True),
mock.call(namespace=self.namespace),
mock.call(namespace=self.namespace)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test_disable_ndp_proxy_update_router(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.router['enable_ndp_proxy'] = False
self.np_ext.update_router(self.context, self.router)
self.add_chain.assert_not_called()
expected_calls = [mock.call(np.DEFAULT_NDP_PROXY_CHAIN)]
self.assertEqual(expected_calls, self.remove_chain.mock_calls)
self.add_rule.assert_not_called()
self.remove_rule.assert_not_called()
flush_cmd = ['ip', '-6', 'neigh', 'flush', 'proxy']
sysctl_cmd = ['sysctl', '-w',
'net.ipv6.conf.%s.proxy_ndp=0' % self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(flush_cmd, check_exit_code=False,
privsep_exec=True),
mock.call().netns.execute(sysctl_cmd, privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
def _test__handle_notification(self):
self.np_ext.add_router(self.context, self.router)
self.add_chain.reset_mock()
self.add_rule.reset_mock()
self.ip_wrapper.reset_mock()
ndpproxy = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.fake_router_id,
port_id=self.port_id, ip_address='2002::1:5')
self.np_ext._handle_notification(mock.MagicMock(), mock.MagicMock(),
[ndpproxy], events.CREATED)
self.add_chain.assert_not_called()
self.remove_chain.assert_not_called()
accept_rule = '-i %s --destination %s -j ACCEPT' % (
self.ext_device_name, '2002::1:5')
expected_calls = [
mock.call(np.DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)]
self.assertEqual(expected_calls, self.add_rule.mock_calls)
self.remove_rule.assert_not_called()
proxy_cmd = ['ip', '-6', 'neigh', 'add', 'proxy',
'2002::1:5', 'dev', self.ext_device_name]
ndsend_cmd = ['ndsend', '2002::1:5', self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True),
mock.call().netns.execute(ndsend_cmd, check_exit_code=False,
log_fail_as_error=True,
privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
self.add_chain.reset_mock()
self.remove_chain.reset_mock()
self.add_rule.reset_mock()
self.remove_rule.reset_mock()
self.ip_wrapper.reset_mock()
self.np_ext._handle_notification(mock.MagicMock(), mock.MagicMock(),
[ndpproxy], events.DELETED)
self.add_chain.assert_not_called()
self.remove_chain.assert_not_called()
self.add_rule.assert_not_called()
accept_rule = ('-i %s --destination %s -j ACCEPT') % (
self.ext_device_name, '2002::1:5')
expected_calls = [
mock.call(np.DEFAULT_NDP_PROXY_CHAIN, accept_rule, top=True)]
self.assertEqual(expected_calls, self.remove_rule.mock_calls)
proxy_cmd = ['ip', '-6', 'neigh', 'del', 'proxy', '2002::1:5',
'dev', self.ext_device_name]
expected_calls = [
mock.call(namespace=self.namespace),
mock.call().netns.execute(proxy_cmd, privsep_exec=True)]
self.assertEqual(expected_calls, self.ip_wrapper.mock_calls)
class NDPProxyExtensionLegacyTestCase(
NDPProxyExtensionLegacyDVRNoExternalTestCaseBase):
def setUp(self):
super(NDPProxyExtensionLegacyTestCase, self).setUp()
self.conf.host = HOSTNAME
self.conf.agent_mode = lib_const.L3_AGENT_MODE_LEGACY
self.agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
self.ext_device_name = 'qg-%s' % self.ext_port_id[0:11]
self.internal_ports = [{'subnets': [{'cidr': '2001::1:0/112'}]}]
self.router = {'id': self.fake_router_id,
'gw_port': self.ex_gw_port,
'ha': False,
'distributed': False,
'enable_ndp_proxy': True}
self.router_info = router_info.RouterInfo(
self.agent, self.fake_router_id, self.router, **self.ri_kwargs)
self.iptables_manager = self.router_info.iptables_manager
self.router_info.internal_ports = self.internal_ports
self.router_info.ex_gw_port = self.ex_gw_port
self.iptables_manager.ipv6['filter'].chains = []
self.iptables_manager.ipv6['filter'].rules = []
self.agent.router_info[self.router['id']] = self.router_info
self.wrap_name = self.iptables_manager.wrap_name
self.namespace = "qrouter-" + self.fake_router_id
self._mock_iptables_actions()
self.ip_wrapper.reset_mock()
def test_create_router(self):
self._test_create_router()
def test_update_router(self):
self._test_update_router()
def test_add_ndp_proxy_update_router(self):
self._test_add_ndp_proxy_update_router()
def test_del_ndp_proxy_update_router(self):
self._test_del_ndp_proxy_update_router()
def test_add_subnet_update_router(self):
self._test_add_subnet_update_router()
def test_remove_subnet_update_router(self):
self._test_remove_subnet_update_router()
def test_disable_ndp_proxy_update_router(self):
self._test_disable_ndp_proxy_update_router()
def test__handle_notification(self):
self._test__handle_notification()
class NDPProxyExtensionDVRNoExternalTestCase(
NDPProxyExtensionLegacyDVRNoExternalTestCaseBase):
def setUp(self):
super(NDPProxyExtensionLegacyDVRNoExternalTestCaseBase, self).setUp()
self.conf.host = HOSTNAME
self.conf.agent_mode = lib_const.L3_AGENT_MODE_DVR_SNAT
self.agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
self.ext_device_name = 'qg-%s' % self.ext_port_id[0:11]
self.internal_ports = [{'subnets': [{'cidr': '2001::1:0/112'}]}]
self.router = {'id': self.fake_router_id,
'gw_port': self.ex_gw_port,
'gw_port_host': HOSTNAME,
'ha': False,
'distributed': True,
'enable_ndp_proxy': True}
interface_driver = mock.Mock()
interface_driver.DEV_NAME_LEN = 14
kwargs = {
'agent': self.agent,
'router_id': self.fake_router_id,
'router': self.router,
'agent_conf': self.conf,
'interface_driver': interface_driver}
self._mock_load_fip = mock.patch.object(
dvr_edge_router.DvrEdgeRouter,
'_load_used_fip_information').start()
self.router_info = dvr_edge_router.DvrEdgeRouter(HOSTNAME, **kwargs)
self.iptables_manager = iptables_manager.IptablesManager(
namespace=self.router_info.snat_namespace.name,
use_ipv6=self.router_info.use_ipv6)
self.router_info.snat_iptables_manager = self.iptables_manager
self.router_info.internal_ports = self.internal_ports
self.router_info.ex_gw_port = self.ex_gw_port
self.iptables_manager.ipv6['filter'].chains = []
self.iptables_manager.ipv6['filter'].rules = []
self.agent.router_info[self.router['id']] = self.router_info
self.wrap_name = self.iptables_manager.wrap_name
self.namespace = "snat-" + self.fake_router_id
self._mock_iptables_actions()
self.ip_wrapper.reset_mock()
def test_create_router(self):
self._test_create_router()
def test_update_router(self):
self._test_update_router()
def test_add_ndp_proxy_update_router(self):
self._test_add_ndp_proxy_update_router()
def test_del_ndp_proxy_update_router(self):
self._test_del_ndp_proxy_update_router()
def test_add_subnet_update_router(self):
self._test_add_subnet_update_router()
def test_remove_subnet_update_router(self):
self._test_remove_subnet_update_router()
def test_disable_ndp_proxy_update_router(self):
self._test_disable_ndp_proxy_update_router()
def test__handle_notification(self):
self._test__handle_notification()
class NDPProxyExtensionInitializeTestCase(NDPProxyExtensionTestCaseBase):
@mock.patch.object(registry, 'register')
@mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback')
def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock):
call_to_patch = 'neutron_lib.rpc.Connection'
with mock.patch(call_to_patch,
return_value=self.connection) as create_connection:
self.np_ext.initialize(
self.connection, lib_const.L3_AGENT_MODE)
create_connection.assert_has_calls([mock.call()])
self.connection.create_consumer.assert_has_calls(
[mock.call(
resources_rpc.resource_type_versioned_topic(
resources.NDPPROXY),
[rpc_mock()],
fanout=True)]
)
subscribe_mock.assert_called_with(
mock.ANY, resources.NDPPROXY)
class RouterNDPProxyMappingTestCase(base.BaseTestCase):
def setUp(self):
super(RouterNDPProxyMappingTestCase, self).setUp()
self.mapping = np.RouterNDPProxyMapping()
self.router1 = _uuid()
self.router2 = _uuid()
self.ndpproxy1 = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.router1,
port_id=_uuid(), ip_address='2002::1:3')
self.ndpproxy2 = np_obj.NDPProxy(
context=None, id=_uuid(),
router_id=self.router2,
port_id=_uuid(), ip_address='2002::1:4')
self.ndpproxies = [self.ndpproxy1, self.ndpproxy2]
def test_set_ndp_proxies(self):
self.mapping.set_ndp_proxies(self.ndpproxies)
for ndp_proxy in self.ndpproxies:
res = self.mapping.get_ndp_proxy(ndp_proxy.id)
self.assertEqual(ndp_proxy, res)
router1_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router1)
self.assertEqual([self.ndpproxy1], router1_ndp_proxies)
router2_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router2)
self.assertEqual([self.ndpproxy2], router2_ndp_proxies)
def test_del_ndp_proxies(self):
self.mapping.set_ndp_proxies(self.ndpproxies)
self.mapping.del_ndp_proxies([self.ndpproxy2])
res = self.mapping.get_ndp_proxy(self.ndpproxy2.id)
self.assertIsNone(res)
router1_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router1)
self.assertEqual([self.ndpproxy1], router1_ndp_proxies)
router2_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router2)
self.assertEqual([], router2_ndp_proxies)
def test_clear_by_router_id(self):
self.mapping.set_ndp_proxies(self.ndpproxies)
self.mapping.clear_by_router_id(self.router1)
np1 = self.mapping.get_ndp_proxy(self.ndpproxy1.id)
self.assertIsNone(np1)
np2 = self.mapping.get_ndp_proxy(self.ndpproxy2.id)
self.assertEqual(self.ndpproxy2, np2)
router1_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router1)
self.assertEqual([], router1_ndp_proxies)
router2_ndp_proxies = self.mapping.get_ndp_proxies_by_router_id(
self.router2)
self.assertEqual([self.ndpproxy2], router2_ndp_proxies)

View File

@ -149,6 +149,10 @@ class BasicRouterOperationsFramework(base.BaseTestCase):
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.lladdr = "fe80::f816:3eff:fe5f:9d67"
get_ipv6_lladdr = mock.patch("neutron.agent.linux.ip_lib."
"get_ipv6_lladdr").start()
get_ipv6_lladdr.return_value = "%s/64" % self.lladdr
self.l3pluginApi_cls_p = mock.patch(
'neutron.agent.l3.agent.L3PluginApi')

View File

@ -43,6 +43,10 @@ class TestDvrFipNs(base.BaseTestCase):
self.conf,
self.driver,
use_ipv6=True)
self.lladdr = "fe80::f816:3eff:fe5f:9d67"
get_ipv6_lladdr = mock.patch("neutron.agent.linux.ip_lib."
"get_ipv6_lladdr").start()
get_ipv6_lladdr.return_value = "%s/64" % self.lladdr
def test_subscribe(self):
is_first = self.fip_ns.subscribe(mock.sentinel.external_net_id)
@ -276,8 +280,6 @@ class TestDvrFipNs(base.BaseTestCase):
ri.ns_name = mock.sentinel.router_ns
ri.get_ex_gw_port.return_value = {'mtu': 2000}
rtr_2_fip_name = self.fip_ns.get_rtr_ext_device_name(ri.router_id)
fip_2_rtr_name = self.fip_ns.get_int_device_name(ri.router_id)
fip_ns_name = self.fip_ns.get_name()
self.fip_ns.local_subnets = allocator = mock.Mock()
@ -294,8 +296,8 @@ class TestDvrFipNs(base.BaseTestCase):
self.fip_ns.create_rtr_2_fip_link(ri)
if not dev_exists:
ip_wrapper.add_veth.assert_called_with(rtr_2_fip_name,
fip_2_rtr_name,
ip_wrapper.add_veth.assert_called_with(device.name,
device.name,
fip_ns_name)
self.assertEqual(2, device.link.set_up.call_count)
@ -314,8 +316,10 @@ class TestDvrFipNs(base.BaseTestCase):
device.neigh.add.assert_has_calls(expected)
self.assertEqual(2, device.neigh.add.call_count)
device.route.add_gateway.assert_called_once_with(
'169.254.31.29', table=16)
expected_calls = [mock.call('169.254.31.29', table=16),
mock.call(self.lladdr)]
self.assertEqual(expected_calls,
device.route.add_gateway.mock_calls)
self.assertTrue(
self.fip_ns._add_rtr_ext_route_rule_to_route_table.called)

View File

@ -140,6 +140,7 @@ neutron.agent.l3.extensions =
port_forwarding = neutron.agent.l3.extensions.port_forwarding:PortForwardingAgentExtension
snat_log = neutron.agent.l3.extensions.snat_log:SNATLoggingExtension
conntrack_helper = neutron.agent.l3.extensions.conntrack_helper:ConntrackHelperAgentExtension
ndp_proxy = neutron.agent.l3.extensions.ndp_proxy:NDPProxyAgentExtension
neutron.services.logapi.drivers =
ovs = neutron.services.logapi.drivers.openvswitch.ovs_firewall_log:OVSFirewallLoggingDriver
neutron.qos.agent_drivers =