Browse Source

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.

Change-Id: Icc995f7b2b465921e41342711d17539f16ead0ce
Closes-Bug: #1892362
(cherry picked from commit 81d375d39a)
changes/71/747871/1
Slawek Kaplonski 1 month ago
parent
commit
b934ae57f1
2 changed files with 62 additions and 10 deletions
  1. +15
    -10
      neutron/agent/linux/pd.py
  2. +47
    -0
      neutron/tests/unit/agent/l3/test_agent.py

+ 15
- 10
neutron/agent/linux/pd.py View File

@@ -27,6 +27,7 @@ from oslo_utils import netutils
import six
from stevedore import driver

from neutron.agent.linux import ip_lib
from neutron.common import utils

LOG = logging.getLogger(__name__)
@@ -209,16 +210,20 @@ class PrefixDelegation(object):

def _add_lla(self, router, lla_with_mask):
if router['gw_interface']:
self.intf_driver.add_ipv6_addr(router['gw_interface'],
lla_with_mask,
router['ns_name'],
'link')
# There is a delay before the LLA becomes active.
# This is because the kernel runs DAD to make sure LLA uniqueness
# Spawn a thread to wait for the interface to be ready
self._spawn_lla_thread(router['gw_interface'],
router['ns_name'],
lla_with_mask)
try:
self.intf_driver.add_ipv6_addr(router['gw_interface'],
lla_with_mask,
router['ns_name'],
'link')
# There is a delay before the LLA becomes active.
# This is because the kernel runs DAD to make sure LLA
# uniqueness
# Spawn a thread to wait for the interface to be ready
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):
eventlet.spawn_n(self._ensure_lla_task,


+ 47
- 0
neutron/tests/unit/agent/l3/test_agent.py View File

@@ -3815,6 +3815,53 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
self._pd_assert_dibbler_calls(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')
def test_pd_generate_dibbler_conf(self, mock_chmod):
pddib = dibbler.PDDibbler("router_id", "subnet-id", "ifname")


Loading…
Cancel
Save