Merge "Add PD support in HA router"

This commit is contained in:
Jenkins 2017-03-15 07:25:35 +00:00 committed by Gerrit Code Review
commit 6dba921f2d
9 changed files with 386 additions and 87 deletions

View File

@ -14,3 +14,4 @@
# prefix_delegation_agent
dibbler-client: CommandFilter, dibbler-client, root
kill_dibbler-client: KillFilter, root, dibbler-client, -9

View File

@ -125,6 +125,7 @@ class AgentMixin(object):
if self.conf.enable_metadata_proxy:
self._update_metadata_proxy(ri, router_id, state)
self._update_radvd_daemon(ri, state)
self.pd.process_ha_state(router_id, state == 'master')
self.state_change_notifier.queue_event((router_id, state))
def _configure_ipv6_ra_on_ext_gw_port_if_necessary(self, ri, state):

View File

@ -95,6 +95,15 @@ class HaRouter(router.RouterInfo):
def ha_namespace(self):
return self.ns_name
def is_router_master(self):
"""this method is normally called before the ha_router object is fully
initialized
"""
if self.router.get('_ha_state') == 'active':
return True
else:
return False
def initialize(self, process_monitor):
super(HaRouter, self).initialize(process_monitor)
ha_port = self.router.get(n_consts.HA_INTERFACE_KEY)

View File

@ -116,6 +116,9 @@ class RouterInfo(object):
# enable_snat by default if it wasn't specified by plugin
self._snat_enabled = self._router.get('enable_snat', True)
def is_router_master(self):
return True
def get_internal_device_name(self, port_id):
return (INTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN]

View File

@ -51,7 +51,13 @@ iface {{ interface_name }} {
# Bind to generated LLA
bind-to-address {{ bind_address }}
# ask for address
{% if hint_prefix != None %}
pd 1 {
prefix {{ hint_prefix }}
}
{% else %}
pd 1
{% endif %}
}
""")
@ -77,7 +83,7 @@ class PDDibbler(pd_driver.PDDriverBase):
def _is_dibbler_client_running(self):
return utils.get_value_from_file(self.pid_path)
def _generate_dibbler_conf(self, ex_gw_ifname, lla):
def _generate_dibbler_conf(self, ex_gw_ifname, lla, hint_prefix):
dcwa = self.dibbler_client_working_area
script_path = utils.get_conf_file_name(dcwa, 'notify', 'sh', True)
buf = six.StringIO()
@ -94,7 +100,8 @@ class PDDibbler(pd_driver.PDDriverBase):
va_id='0x%s' % self.converted_subnet_id,
script_path='"%s/notify.sh"' % dcwa,
interface_name='"%s"' % ex_gw_ifname,
bind_address='%s' % lla))
bind_address='%s' % lla,
hint_prefix=hint_prefix))
file_utils.replace_file(dibbler_conf, buf.getvalue())
return dcwa
@ -118,17 +125,18 @@ class PDDibbler(pd_driver.PDDriverBase):
service_name=PD_SERVICE_NAME,
monitored_process=pm)
def enable(self, pmon, router_ns, ex_gw_ifname, lla):
def enable(self, pmon, router_ns, ex_gw_ifname, lla, prefix=None):
LOG.debug("Enable IPv6 PD for router %s subnet %s ri_ifname %s",
self.router_id, self.subnet_id, self.ri_ifname)
if not self._is_dibbler_client_running():
dibbler_conf = self._generate_dibbler_conf(ex_gw_ifname, lla)
dibbler_conf = self._generate_dibbler_conf(ex_gw_ifname,
lla, prefix)
self._spawn_dibbler(pmon, router_ns, dibbler_conf)
LOG.debug("dibbler client enabled for router %s subnet %s"
" ri_ifname %s",
self.router_id, self.subnet_id, self.ri_ifname)
def disable(self, pmon, router_ns):
def disable(self, pmon, router_ns, switch_over=False):
LOG.debug("Disable IPv6 PD for router %s subnet %s ri_ifname %s",
self.router_id, self.subnet_id, self.ri_ifname)
dcwa = self.dibbler_client_working_area
@ -147,7 +155,10 @@ class PDDibbler(pd_driver.PDDriverBase):
service=PD_SERVICE_NAME,
conf=cfg.CONF,
pid_file=self.pid_path)
pm.disable(get_stop_command=callback)
if switch_over:
pm.disable()
else:
pm.disable(get_stop_command=callback)
shutil.rmtree(dcwa, ignore_errors=True)
LOG.debug("dibbler client disabled for router %s subnet %s "
"ri_ifname %s",

View File

@ -65,6 +65,9 @@ class PrefixDelegation(object):
events.AFTER_DELETE)
self._get_sync_data()
def _is_pd_master_router(self, router):
return router['master']
@utils.synchronized("l3-agent-pd")
def enable_subnet(self, router_id, subnet_id, prefix, ri_ifname, mac):
router = self.routers.get(router_id)
@ -80,10 +83,12 @@ class PrefixDelegation(object):
if pd_info.sync:
pd_info.mac = mac
pd_info.old_prefix = prefix
else:
elif self._is_pd_master_router(router):
self._add_lla(router, pd_info.get_bind_lla_with_mask())
def _delete_pd(self, router, pd_info):
if not self._is_pd_master_router(router):
return
self._delete_lla(router, pd_info.get_bind_lla_with_mask())
if pd_info.client_started:
pd_info.driver.disable(self.pmon, router['ns_name'])
@ -98,10 +103,11 @@ class PrefixDelegation(object):
if not pd_info:
return
self._delete_pd(router, pd_info)
prefix_update[subnet_id] = l3_constants.PROVISIONAL_IPV6_PD_PREFIX
if self._is_pd_master_router(router):
prefix_update[subnet_id] = l3_constants.PROVISIONAL_IPV6_PD_PREFIX
LOG.debug("Update server with prefixes: %s", prefix_update)
self.notifier(self.context, prefix_update)
del router['subnets'][subnet_id]
LOG.debug("Update server with prefixes: %s", prefix_update)
self.notifier(self.context, prefix_update)
@utils.synchronized("l3-agent-pd")
def update_subnet(self, router_id, subnet_id, prefix):
@ -111,16 +117,19 @@ class PrefixDelegation(object):
if pd_info and pd_info.old_prefix != prefix:
old_prefix = pd_info.old_prefix
pd_info.old_prefix = prefix
pd_info.prefix = prefix
return old_prefix
@utils.synchronized("l3-agent-pd")
def add_gw_interface(self, router_id, gw_ifname):
router = self.routers.get(router_id)
prefix_update = {}
if not router:
return
router['gw_interface'] = gw_ifname
for subnet_id, pd_info in six.iteritems(router['subnets']):
if not self._is_pd_master_router(router):
return
prefix_update = {}
for pd_info in six.itervalues(router['subnets']):
# gateway is added after internal router ports.
# If a PD is being synced, and if the prefix is available,
# send update if prefix out of sync; If not available,
@ -141,6 +150,8 @@ class PrefixDelegation(object):
self.notifier(self.context, prefix_update)
def delete_router_pd(self, router):
if not self._is_pd_master_router(router):
return
prefix_update = {}
for subnet_id, pd_info in six.iteritems(router['subnets']):
self._delete_lla(router, pd_info.get_bind_lla_with_mask())
@ -166,7 +177,7 @@ class PrefixDelegation(object):
preserve_ips = []
router = self.routers.get(router_id)
if router is not None:
for subnet_id, pd_info in router['subnets'].items():
for pd_info in six.itervalues(router['subnets']):
preserve_ips.append(pd_info.get_bind_lla_with_mask())
return preserve_ips
@ -180,7 +191,7 @@ class PrefixDelegation(object):
def remove_stale_ri_ifname(self, router_id, stale_ifname):
router = self.routers.get(router_id)
if router is not None:
for subnet_id, pd_info in router['subnets'].items():
for subnet_id, pd_info in six.iteriterms(router['subnets']):
if pd_info.ri_ifname == stale_ifname:
self._delete_pd(router, pd_info)
del router['subnets'][subnet_id]
@ -253,13 +264,34 @@ class PrefixDelegation(object):
return not lla['tentative']
return False
@utils.synchronized("l3-agent-pd")
def process_ha_state(self, router_id, master):
router = self.routers.get(router_id)
if router is None or router['master'] == master:
return
router['master'] = master
if master:
for pd_info in six.itervalues(router['subnets']):
bind_lla_with_mask = pd_info.get_bind_lla_with_mask()
self._add_lla(router, bind_lla_with_mask)
else:
for pd_info in six.itervalues(router['subnets']):
self._delete_lla(router, pd_info.get_bind_lla_with_mask())
if pd_info.client_started:
pd_info.driver.disable(self.pmon,
router['ns_name'],
switch_over=True)
pd_info.client_started = False
@utils.synchronized("l3-agent-pd")
def process_prefix_update(self):
LOG.debug("Processing IPv6 PD Prefix Update")
prefix_update = {}
for router_id, router in six.iteritems(self.routers):
if not router['gw_interface']:
if not (self._is_pd_master_router(router) and
router['gw_interface']):
continue
llas = None
@ -279,9 +311,15 @@ class PrefixDelegation(object):
if not pd_info.driver:
pd_info.driver = self.pd_dhcp_driver(
router_id, subnet_id, pd_info.ri_ifname)
prefix = None
if (pd_info.prefix !=
l3_constants.PROVISIONAL_IPV6_PD_PREFIX):
prefix = pd_info.prefix
pd_info.driver.enable(self.pmon, router['ns_name'],
router['gw_interface'],
pd_info.bind_lla)
pd_info.bind_lla,
prefix)
pd_info.client_started = True
if prefix_update:
@ -305,7 +343,8 @@ class PrefixDelegation(object):
for pd_info in sync_data:
router_id = pd_info.router_id
if not self.routers.get(router_id):
self.routers[router_id] = {'gw_interface': None,
self.routers[router_id] = {'master': True,
'gw_interface': None,
'ns_name': None,
'subnets': {}}
new_pd_info = PDInfo(pd_info=pd_info)
@ -322,8 +361,9 @@ def remove_router(resource, event, l3_agent, **kwargs):
del l3_agent.pd.routers[router_id]
def get_router_entry(ns_name):
return {'gw_interface': None,
def get_router_entry(ns_name, master):
return {'master': master,
'gw_interface': None,
'ns_name': ns_name,
'subnets': {}}
@ -333,12 +373,14 @@ def add_router(resource, event, l3_agent, **kwargs):
added_router = kwargs['router']
router = l3_agent.pd.routers.get(added_router.router_id)
gw_ns_name = added_router.get_gw_ns_name()
master = added_router.is_router_master()
if not router:
l3_agent.pd.routers[added_router.router_id] = (
get_router_entry(gw_ns_name))
get_router_entry(gw_ns_name, master))
else:
# This will happen during l3 agent restart
router['ns_name'] = gw_ns_name
router['master'] = master
@utils.synchronized("l3-agent-pd")

View File

@ -20,6 +20,7 @@ from oslo_utils import uuidutils
from six import moves
from neutron.common import constants as n_const
from neutron.common import ipv6_utils
_uuid = uuidutils.generate_uuid
@ -287,6 +288,26 @@ def router_append_pd_enabled_subnet(router, count=1):
interfaces.append(intf)
pd_intfs.append(intf)
mac_address.value += 1
def get_unassigned_pd_interfaces(router):
pd_intfs = []
for intf in router[lib_constants.INTERFACE_KEY]:
for subnet in intf['subnets']:
if (ipv6_utils.is_ipv6_pd_enabled(subnet) and
subnet['cidr'] == n_const.PROVISIONAL_IPV6_PD_PREFIX):
pd_intfs.append(intf)
return pd_intfs
def assign_prefix_for_pd_interfaces(router):
pd_intfs = []
for ifno, intf in enumerate(router[lib_constants.INTERFACE_KEY]):
for subnet in intf['subnets']:
if (ipv6_utils.is_ipv6_pd_enabled(subnet) and
subnet['cidr'] == n_const.PROVISIONAL_IPV6_PD_PREFIX):
subnet['cidr'] = "2001:db8:%d::/64" % ifno
pd_intfs.append(intf)
return pd_intfs

View File

@ -2550,7 +2550,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
expected += "%s " % dns
self.assertIn(expected, self.utils_replace_file.call_args[0][1])
def _pd_expected_call_external_process(self, requestor, ri, enable=True):
def _pd_expected_call_external_process(self, requestor, ri,
enable=True, ha=False):
expected_calls = []
if enable:
expected_calls.append(mock.call(uuid=requestor,
@ -2566,34 +2567,37 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
namespace=ri.ns_name,
conf=mock.ANY,
pid_file=mock.ANY))
expected_calls.append(mock.call().disable(
get_stop_command=mock.ANY))
# in the HA switchover case, disable is called without arguments
if ha:
expected_calls.append(mock.call().disable())
else:
expected_calls.append(mock.call().disable(
get_stop_command=mock.ANY))
return expected_calls
def _pd_setup_agent_router(self):
def _pd_setup_agent_router(self, enable_ha=False):
router = l3_test_common.prepare_router_data()
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3router.RouterInfo(agent, router['id'],
router, **self.ri_kwargs)
ri.iptables_manager.ipv6['mangle'] = mock.MagicMock()
ri._process_pd_iptables_rules = mock.MagicMock()
agent.external_gateway_added = mock.Mock()
ri.process()
agent._router_added(router['id'], router)
# Make sure radvd monitor is created
ri = agent.router_info[router['id']]
ri.iptables_manager.ipv6['mangle'] = mock.MagicMock()
ri._process_pd_iptables_rules = mock.MagicMock()
if not ri.radvd:
ri.radvd = ra.DaemonMonitor(router['id'],
ri.ns_name,
agent.process_monitor,
ri.get_internal_device_name,
self.conf)
if enable_ha:
agent.pd.routers[router['id']]['master'] = False
return agent, router, ri
def _pd_remove_gw_interface(self, intfs, agent, router, ri):
def _pd_remove_gw_interface(self, intfs, agent, ri):
expected_pd_update = {}
expected_calls = []
for intf in intfs:
requestor_id = self._pd_get_requestor_id(intf, router, ri)
requestor_id = self._pd_get_requestor_id(intf, ri)
expected_calls += (self._pd_expected_call_external_process(
requestor_id, ri, False))
for subnet in intf['subnets']:
@ -2616,19 +2620,19 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
# Remove the gateway interface
agent.pd.notifier = pd_notifier
agent.pd.remove_gw_interface(router['id'])
agent.pd.remove_gw_interface(ri.router['id'])
self._pd_assert_dibbler_calls(expected_calls,
self.external_process.mock_calls[-len(expected_calls):])
self.assertEqual(expected_pd_update, self.pd_update)
def _pd_remove_interfaces(self, intfs, agent, router, ri):
def _pd_remove_interfaces(self, intfs, agent, ri):
expected_pd_update = []
expected_calls = []
for intf in intfs:
# Remove the router interface
router[lib_constants.INTERFACE_KEY].remove(intf)
requestor_id = self._pd_get_requestor_id(intf, router, ri)
ri.router[lib_constants.INTERFACE_KEY].remove(intf)
requestor_id = self._pd_get_requestor_id(intf, ri)
expected_calls += (self._pd_expected_call_external_process(
requestor_id, ri, False))
for subnet in intf['subnets']:
@ -2659,10 +2663,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self._pd_assert_radvd_calls(ri, False)
self.assertEqual(expected_pd_update, self.pd_update)
def _pd_get_requestor_id(self, intf, router, ri):
def _pd_get_requestor_id(self, intf, ri):
ifname = ri.get_internal_device_name(intf['id'])
for subnet in intf['subnets']:
return dibbler.PDDibbler(router['id'],
return dibbler.PDDibbler(ri.router['id'],
subnet['id'], ifname).requestor_id
def _pd_assert_dibbler_calls(self, expected, actual):
@ -2698,7 +2702,14 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self.assertEqual(exp_calls,
self.external_process.mock_calls[-len(exp_calls):])
def _pd_get_prefixes(self, agent, router, ri,
def _pd_assert_update_subnet_calls(self, router_id, intfs,
mock_pd_update_subnet):
for intf in intfs:
mock_pd_update_subnet.assert_any_call(router_id,
intf['subnets'][0]['id'],
intf['subnets'][0]['cidr'])
def _pd_get_prefixes(self, agent, ri,
existing_intfs, new_intfs, mock_get_prefix):
# First generate the prefixes that will be used for each interface
prefixes = {}
@ -2706,8 +2717,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
expected_calls = []
last_prefix = ''
for ifno, intf in enumerate(existing_intfs + new_intfs):
requestor_id = self._pd_get_requestor_id(intf, router, ri)
prefixes[requestor_id] = "2001:cafe:cafe:%d::/64" % ifno
requestor_id = self._pd_get_requestor_id(intf, ri)
prefixes[requestor_id] = "2001:db8:%d::/64" % ifno
last_prefix = prefixes[requestor_id]
if intf in new_intfs:
subnet_id = (intf['subnets'][0]['id'] if intf['subnets']
@ -2723,11 +2734,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
def pd_notifier(context, prefix_update):
self.pd_update = prefix_update
for subnet_id, prefix in six.iteritems(prefix_update):
gateway_ip = '%s1' % netaddr.IPNetwork(prefix).network
for intf in new_intfs:
for fip in intf['fixed_ips']:
if fip['subnet_id'] == subnet_id:
fip['ip_address'] = gateway_ip
for subnet in intf['subnets']:
if subnet['id'] == subnet_id:
# Update the prefix
subnet['cidr'] = prefix
subnet['gateway_ip'] = gateway_ip
break
# Start the dibbler client
@ -2748,10 +2764,26 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
return last_prefix
def _pd_add_gw_interface(self, agent, router, ri):
gw_ifname = ri.get_external_device_name(router['gw_port']['id'])
agent.pd.add_gw_interface(router['id'], gw_ifname)
def _pd_verify_update_results(self, ri, pd_intfs, mock_pd_update_subnet):
# verify router port initialized
for intf in pd_intfs:
self.mock_driver.init_router_port.assert_any_call(
ri.get_internal_device_name(intf['id']),
ip_cidrs=l3router.common_utils.fixed_ip_cidrs(
intf['fixed_ips']),
namespace=ri.ns_name)
# verify that subnet is updated in PD
self._pd_assert_update_subnet_calls(ri.router['id'], pd_intfs,
mock_pd_update_subnet)
# Check that radvd is started
self._pd_assert_radvd_calls(ri)
def _pd_add_gw_interface(self, agent, ri):
gw_ifname = ri.get_external_device_name(ri.router['gw_port']['id'])
agent.pd.add_gw_interface(ri.router['id'], gw_ifname)
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
@ -2760,7 +2792,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_add_remove_subnet(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix):
mock_getpid, mock_get_prefix,
mock_pd_update_subnet):
'''Add and remove one pd-enabled subnet
Remove the interface by deleting it from the router
'''
@ -2768,8 +2801,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent, router, ri = self._pd_setup_agent_router()
# Create one pd-enabled subnet and add router interface
intfs = l3_test_common.router_append_pd_enabled_subnet(router)
subnet_id = intfs[0]['subnets'][0]['id']
l3_test_common.router_append_pd_enabled_subnet(router)
ri.process()
# No client should be started since there is no gateway port
@ -2777,18 +2809,21 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self.assertFalse(mock_get_prefix.call_count)
# Add the gateway interface
self._pd_add_gw_interface(agent, router, ri)
self._pd_add_gw_interface(agent, ri)
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
subnet_id = pd_intfs[0]['subnets'][0]['id']
# Get one prefix
prefix = self._pd_get_prefixes(agent, router, ri, [],
intfs, mock_get_prefix)
prefix = self._pd_get_prefixes(agent, ri, [],
pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
# Check that radvd is started and the router port is configured
# with the new prefix
self._pd_assert_radvd_calls(ri)
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
# Check that _process_pd_iptables_rules() is called correctly
self.assertEqual({subnet_id: prefix}, ri.pd_subnets)
@ -2796,9 +2831,10 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
subnet_id)
# Now remove the interface
self._pd_remove_interfaces(intfs, agent, router, ri)
self._pd_remove_interfaces(pd_intfs, agent, ri)
self.assertEqual({}, ri.pd_subnets)
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
@ -2807,7 +2843,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_remove_gateway(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix):
mock_getpid, mock_get_prefix,
mock_pd_update_subnet):
'''Add one pd-enabled subnet and remove the gateway port
Remove the gateway port and check the prefix is removed
'''
@ -2815,27 +2852,28 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent, router, ri = self._pd_setup_agent_router()
# Create one pd-enabled subnet and add router interface
intfs = l3_test_common.router_append_pd_enabled_subnet(router)
l3_test_common.router_append_pd_enabled_subnet(router)
ri.process()
# Add the gateway interface
self._pd_add_gw_interface(agent, router, ri)
self._pd_add_gw_interface(agent, ri)
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
# Get one prefix
self._pd_get_prefixes(agent, router, ri, [], intfs, mock_get_prefix)
self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
# Check that radvd is started
self._pd_assert_radvd_calls(ri)
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
# Now remove the gw interface
self._pd_remove_gw_interface(intfs, agent, router, ri)
# There will be a router update
ri.process()
self._pd_remove_gw_interface(pd_intfs, agent, ri)
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
@ -2844,7 +2882,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_add_remove_2_subnets(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix):
mock_getpid, mock_get_prefix,
mock_pd_update_subnet):
'''Add and remove two pd-enabled subnets
Remove the interfaces by deleting them from the router
'''
@ -2852,7 +2891,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent, router, ri = self._pd_setup_agent_router()
# Create 2 pd-enabled subnets and add router interfaces
intfs = l3_test_common.router_append_pd_enabled_subnet(router, count=2)
l3_test_common.router_append_pd_enabled_subnet(router, count=2)
ri.process()
# No client should be started
@ -2860,21 +2899,24 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self.assertFalse(mock_get_prefix.call_count)
# Add the gateway interface
self._pd_add_gw_interface(agent, router, ri)
self._pd_add_gw_interface(agent, ri)
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
# Get prefixes
self._pd_get_prefixes(agent, router, ri, [], intfs, mock_get_prefix)
self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
# Check that radvd is started and the router port is configured
# with the new prefix
self._pd_assert_radvd_calls(ri)
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
# Now remove the interface
self._pd_remove_interfaces(intfs, agent, router, ri)
self._pd_remove_interfaces(pd_intfs, agent, ri)
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
@ -2883,7 +2925,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_remove_gateway_2_subnets(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix):
mock_getpid, mock_get_prefix,
mock_pd_update_subnet):
'''Add one pd-enabled subnet, followed by adding another one
Remove the gateway port and check the prefix is removed
'''
@ -2891,42 +2934,209 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent, router, ri = self._pd_setup_agent_router()
# Add the gateway interface
self._pd_add_gw_interface(agent, router, ri)
self._pd_add_gw_interface(agent, ri)
# Create 1 pd-enabled subnet and add router interface
intfs = l3_test_common.router_append_pd_enabled_subnet(router, count=1)
l3_test_common.router_append_pd_enabled_subnet(router, count=1)
ri.process()
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
# Get prefixes
self._pd_get_prefixes(agent, router, ri, [], intfs, mock_get_prefix)
self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
# Check that radvd is started
self._pd_assert_radvd_calls(ri)
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
# Now add another interface
# Create one pd-enabled subnet and add router interface
intfs1 = l3_test_common.router_append_pd_enabled_subnet(router,
count=1)
l3_test_common.router_append_pd_enabled_subnet(update_router, count=1)
ri.process()
update_router_2 = copy.deepcopy(update_router)
pd_intfs1 = l3_test_common.get_unassigned_pd_interfaces(
update_router_2)
# Get prefixes
self._pd_get_prefixes(agent, router, ri, intfs,
intfs1, mock_get_prefix)
self._pd_get_prefixes(agent, ri, pd_intfs, pd_intfs1, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router_2
ri.process()
# Check that radvd is notified for the new prefix
self._pd_assert_radvd_calls(ri)
self._pd_verify_update_results(ri, pd_intfs1, mock_pd_update_subnet)
# Now remove the gw interface
self._pd_remove_gw_interface(intfs + intfs1, agent, router, ri)
self._pd_remove_gw_interface(pd_intfs + pd_intfs1, agent, ri)
@mock.patch.object(l3router.RouterInfo, 'enable_radvd')
@mock.patch.object(pd.PrefixDelegation, '_add_lla')
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
return_value=True)
@mock.patch.object(dibbler.os, 'chmod')
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_ha_standby(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix,
mock_pd_update_subnet,
mock_add_lla, mock_enable_radvd):
'''Test HA in the standby router
The intent is to test the PD code with HA. To avoid unnecessary
complexities, use the regular router.
'''
# Initial setup
agent, router, ri = self._pd_setup_agent_router(enable_ha=True)
# Create one pd-enabled subnet and add router interface
l3_test_common.router_append_pd_enabled_subnet(router)
self._pd_add_gw_interface(agent, ri)
ri.process()
self.assertFalse(mock_add_lla.called)
# No client should be started since it's standby router
agent.pd.process_prefix_update()
self.assertFalse(self.external_process.called)
self.assertFalse(mock_get_prefix.called)
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.assign_prefix_for_pd_interfaces(
update_router)
# Update the router with the new prefix
ri.router = update_router
ri.process()
self._pd_assert_update_subnet_calls(router['id'], pd_intfs,
mock_pd_update_subnet)
# No client should be started since it's standby router
agent.pd.process_prefix_update()
self.assertFalse(self.external_process.called)
self.assertFalse(mock_get_prefix.called)
@mock.patch.object(pd.PrefixDelegation, '_add_lla')
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
return_value=True)
@mock.patch.object(dibbler.os, 'chmod')
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_ha_active(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix,
mock_pd_update_subnet,
mock_add_lla):
'''Test HA in the active router
The intent is to test the PD code with HA. To avoid unnecessary
complexities, use the regular router.
'''
# Initial setup
agent, router, ri = self._pd_setup_agent_router(enable_ha=True)
# Create one pd-enabled subnet and add router interface
l3_test_common.router_append_pd_enabled_subnet(router)
self._pd_add_gw_interface(agent, ri)
ri.process()
self.assertFalse(mock_add_lla.called)
# No client should be started since it's standby router
agent.pd.process_prefix_update()
self.assertFalse(self.external_process.called)
self.assertFalse(mock_get_prefix.called)
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
# Turn the router to be active
agent.pd.process_ha_state(router['id'], True)
# Get prefixes
self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
@mock.patch.object(pd.PrefixDelegation, 'update_subnet')
@mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True)
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
@mock.patch.object(pd.PrefixDelegation, '_is_lla_active',
return_value=True)
@mock.patch.object(dibbler.os, 'chmod')
@mock.patch.object(dibbler.shutil, 'rmtree')
@mock.patch.object(pd.PrefixDelegation, '_get_sync_data')
def test_pd_ha_switchover(self, mock1, mock2, mock3, mock4,
mock_getpid, mock_get_prefix,
mock_pd_update_subnet):
'''Test HA in the active router
The intent is to test the PD code with HA. To avoid unnecessary
complexities, use the regular router.
'''
# Initial setup
agent, router, ri = self._pd_setup_agent_router(enable_ha=True)
# Turn the router to be active
agent.pd.process_ha_state(router['id'], True)
# Create one pd-enabled subnet and add router interface
l3_test_common.router_append_pd_enabled_subnet(router)
self._pd_add_gw_interface(agent, ri)
ri.process()
update_router = copy.deepcopy(router)
pd_intfs = l3_test_common.get_unassigned_pd_interfaces(update_router)
# Get prefixes
self._pd_get_prefixes(agent, ri, [], pd_intfs, mock_get_prefix)
# Update the router with the new prefix
ri.router = update_router
ri.process()
self._pd_verify_update_results(ri, pd_intfs, mock_pd_update_subnet)
# Turn the router to be standby
agent.pd.process_ha_state(router['id'], False)
expected_calls = []
for intf in pd_intfs:
requestor_id = self._pd_get_requestor_id(intf, ri)
expected_calls += (self._pd_expected_call_external_process(
requestor_id, ri, False, ha=True))
self._pd_assert_dibbler_calls(expected_calls,
self.external_process.mock_calls[-len(expected_calls):])
@mock.patch.object(dibbler.os, 'chmod')
def test_pd_generate_dibbler_conf(self, mock_chmod):
pddib = dibbler.PDDibbler("router_id", "subnet-id", "ifname")
pddib._generate_dibbler_conf("ex_gw_ifname",
"fe80::f816:3eff:fef5:a04e", None)
expected = 'bind-to-address fe80::f816:3eff:fef5:a04e\n'\
'# ask for address\n \n pd 1\n \n}'
self.assertIn(expected, self.utils_replace_file.call_args[0][1])
pddib._generate_dibbler_conf("ex_gw_ifname",
"fe80::f816:3eff:fef5:a04e",
"2001:db8:2c50:2026::/64")
expected = 'bind-to-address fe80::f816:3eff:fef5:a04e\n'\
'# ask for address\n \n pd 1 '\
'{\n prefix 2001:db8:2c50:2026::/64\n }\n \n}'
self.assertIn(expected, self.utils_replace_file.call_args[0][1])
def _verify_address_scopes_iptables_rule(self, mock_iptables_manager):
filter_calls = [mock.call.add_chain('scope'),
mock.call.add_rule('FORWARD', '-j $scope')]

View File

@ -27,8 +27,9 @@ class FakeRouter(object):
class TestPrefixDelegation(tests_base.DietTestCase):
def test_remove_router(self):
l3_agent = mock.Mock()
router_id = '1'
l3_agent.pd.routers = {router_id: pd.get_router_entry(None)}
router_id = 1
l3_agent.pd.routers = {router_id:
pd.get_router_entry(None, True)}
pd.remove_router(None, None, l3_agent, router=FakeRouter(router_id))
self.assertTrue(l3_agent.pd.delete_router_pd.called)
self.assertEqual({}, l3_agent.pd.routers)