Merge "Add PD support in HA router"
This commit is contained in:
commit
6dba921f2d
@ -14,3 +14,4 @@
|
||||
|
||||
# prefix_delegation_agent
|
||||
dibbler-client: CommandFilter, dibbler-client, root
|
||||
kill_dibbler-client: KillFilter, root, dibbler-client, -9
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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')]
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user