Handle properly existing LLA address during l3 agent restart
In case when L3 agent is hosting routers which have got subnets
with Prefix Delegation enabled, agent couldn't properly handle
IpAddressAlreadyExists exception raised when pd module tries to
configure link local IPv6 addresses.
Now this is fixed and L3 agent can restart without problems in such
case.
Conflicts:
neutron/agent/linux/pd.py
Change-Id: Icc995f7b2b465921e41342711d17539f16ead0ce
Closes-Bug: #1892362
(cherry picked from commit 81d375d39a
)
This commit is contained in:
parent
2e910c5324
commit
714fb4e3d2
|
@ -27,6 +27,7 @@ from oslo_utils import netutils
|
||||||
import six
|
import six
|
||||||
from stevedore import driver
|
from stevedore import driver
|
||||||
|
|
||||||
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.common import constants as l3_constants
|
from neutron.common import constants as l3_constants
|
||||||
from neutron.common import utils
|
from neutron.common import utils
|
||||||
|
|
||||||
|
@ -210,16 +211,20 @@ class PrefixDelegation(object):
|
||||||
|
|
||||||
def _add_lla(self, router, lla_with_mask):
|
def _add_lla(self, router, lla_with_mask):
|
||||||
if router['gw_interface']:
|
if router['gw_interface']:
|
||||||
self.intf_driver.add_ipv6_addr(router['gw_interface'],
|
try:
|
||||||
lla_with_mask,
|
self.intf_driver.add_ipv6_addr(router['gw_interface'],
|
||||||
router['ns_name'],
|
lla_with_mask,
|
||||||
'link')
|
router['ns_name'],
|
||||||
# There is a delay before the LLA becomes active.
|
'link')
|
||||||
# This is because the kernel runs DAD to make sure LLA uniqueness
|
# There is a delay before the LLA becomes active.
|
||||||
# Spawn a thread to wait for the interface to be ready
|
# This is because the kernel runs DAD to make sure LLA
|
||||||
self._spawn_lla_thread(router['gw_interface'],
|
# uniqueness
|
||||||
router['ns_name'],
|
# Spawn a thread to wait for the interface to be ready
|
||||||
lla_with_mask)
|
self._spawn_lla_thread(router['gw_interface'],
|
||||||
|
router['ns_name'],
|
||||||
|
lla_with_mask)
|
||||||
|
except ip_lib.IpAddressAlreadyExists:
|
||||||
|
pass
|
||||||
|
|
||||||
def _spawn_lla_thread(self, gw_ifname, ns_name, lla_with_mask):
|
def _spawn_lla_thread(self, gw_ifname, ns_name, lla_with_mask):
|
||||||
eventlet.spawn_n(self._ensure_lla_task,
|
eventlet.spawn_n(self._ensure_lla_task,
|
||||||
|
|
|
@ -48,6 +48,7 @@ from neutron.agent.l3 import namespaces
|
||||||
from neutron.agent.l3 import router_info as l3router
|
from neutron.agent.l3 import router_info as l3router
|
||||||
from neutron.agent.linux import dibbler
|
from neutron.agent.linux import dibbler
|
||||||
from neutron.agent.linux import interface
|
from neutron.agent.linux import interface
|
||||||
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import iptables_manager
|
from neutron.agent.linux import iptables_manager
|
||||||
from neutron.agent.linux import pd
|
from neutron.agent.linux import pd
|
||||||
from neutron.agent.linux import ra
|
from neutron.agent.linux import ra
|
||||||
|
@ -3862,6 +3863,53 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
self._pd_assert_dibbler_calls(expected_calls,
|
self._pd_assert_dibbler_calls(expected_calls,
|
||||||
self.external_process.mock_calls[-len(expected_calls):])
|
self.external_process.mock_calls[-len(expected_calls):])
|
||||||
|
|
||||||
|
@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_lla_already_exists(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)
|
||||||
|
|
||||||
|
agent.pd.intf_driver = mock.MagicMock()
|
||||||
|
agent.pd.intf_driver.add_ipv6_addr.side_effect = (
|
||||||
|
ip_lib.IpAddressAlreadyExists())
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# 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(dibbler.os, 'chmod')
|
@mock.patch.object(dibbler.os, 'chmod')
|
||||||
def test_pd_generate_dibbler_conf(self, mock_chmod):
|
def test_pd_generate_dibbler_conf(self, mock_chmod):
|
||||||
pddib = dibbler.PDDibbler("router_id", "subnet-id", "ifname")
|
pddib = dibbler.PDDibbler("router_id", "subnet-id", "ifname")
|
||||||
|
|
Loading…
Reference in New Issue