Merge "Setup DVR router ARP for AAP with correct MAC"

This commit is contained in:
Zuul 2025-01-09 16:14:53 +00:00 committed by Gerrit Code Review
commit da06d1a7d8
5 changed files with 118 additions and 12 deletions

View File

@ -319,6 +319,8 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
device_exists=True):
"""Add or delete arp entry into router namespace for the subnet."""
LOG.debug("Handling ARP entry operation %s for ip: %s mac: %s "
"device: %s", operation, ip, mac, device)
try:
if device_exists:
if operation == 'add':

View File

@ -1260,13 +1260,16 @@ class _DVRAgentInterfaceMixin:
aa_pair_fixed_ips = []
if port_dict.get('allowed_address_pairs'):
for address_pair in port_dict['allowed_address_pairs']:
aap_mac_address = address_pair.get("mac_address",
port_dict["mac_address"])
aap_ip_cidr = address_pair['ip_address'].split("/")
if len(aap_ip_cidr) == 1 or int(aap_ip_cidr[1]) == 32:
subnet_id = self._get_subnet_id_for_given_fixed_ip(
context, aap_ip_cidr[0], port_dict)
if subnet_id is not None:
fixed_ip = {'subnet_id': subnet_id,
'ip_address': aap_ip_cidr[0]}
'ip_address': aap_ip_cidr[0],
'mac_address': aap_mac_address}
aa_pair_fixed_ips.append(fixed_ip)
else:
LOG.debug("Subnet does not match for the given "
@ -1289,8 +1292,9 @@ class _DVRAgentInterfaceMixin:
self._get_allowed_address_pair_fixed_ips(context, port_dict))
changed_fixed_ips = fixed_ips + allowed_address_pair_fixed_ips
for fixed_ip in changed_fixed_ips:
mac_address = fixed_ip.get("mac_address", port_dict['mac_address'])
self._generate_arp_table_and_notify_agent(
context, fixed_ip, port_dict['mac_address'],
context, fixed_ip, mac_address,
self.l3_rpc_notifier.add_arp_entry)
def delete_arp_entry_for_dvr_service_port(self, context, port_dict,
@ -1311,8 +1315,9 @@ class _DVRAgentInterfaceMixin:
self._get_allowed_address_pair_fixed_ips(context, port_dict))
fixed_ips_to_delete = fixed_ips + allowed_address_pair_fixed_ips
for fixed_ip in fixed_ips_to_delete:
mac_address = fixed_ip.get("mac_address", port_dict['mac_address'])
self._generate_arp_table_and_notify_agent(
context, fixed_ip, port_dict['mac_address'],
context, fixed_ip, mac_address,
self.l3_rpc_notifier.del_arp_entry)
def _get_address_pair_active_port_with_fip(

View File

@ -1183,7 +1183,7 @@ class L3DvrTestCase(L3DvrTestCaseBase):
vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id']
vrrp_arp_table1 = {
'ip_address': vrrp_port_fixed_ips[0]['ip_address'],
'mac_address': vm_port_mac,
'mac_address': vrrp_port['port']['mac_address'],
'subnet_id': vrrp_port_subnet_id}
expected_calls = [
@ -1335,7 +1335,7 @@ class L3DvrTestCase(L3DvrTestCaseBase):
vrrp_port_subnet_id = vrrp_port_fixed_ips[0]['subnet_id']
vrrp_arp_table1 = {
'ip_address': vrrp_port_fixed_ips[0]['ip_address'],
'mac_address': vm_port_mac,
'mac_address': vrrp_port['port']['mac_address'],
'subnet_id': vrrp_port_subnet_id}
expected_calls = [

View File

@ -1337,8 +1337,7 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
mock.call(self.ctx, "router_1", expected_arp_table),
mock.call(self.ctx, "router_2", expected_arp_table)])
def _test_update_arp_entry_for_dvr_service_port(
self, device_owner, action):
def _test_arp_entry_for_dvr_service_port_no_aap(self, action):
router_dict = {'name': 'test_router', 'admin_state_up': True,
'distributed': True}
router = self._create_router(router_dict)
@ -1355,7 +1354,8 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
{'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b',
'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111'}],
'mac_address': 'my_mac',
'device_owner': device_owner
'device_owner': 'nova:compute',
'allowed_address_pairs': []
}
dvr_port = {
'id': 'dvr_port_id',
@ -1364,14 +1364,105 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
'device_id': router['id']
}
plugin.get_ports.return_value = [dvr_port]
if action == 'add':
if action == 'update':
self.mixin.update_arp_entry_for_dvr_service_port(
self.ctx, port)
self.assertEqual(3, l3_notify.add_arp_entry.call_count)
elif action == 'del':
elif action == 'delete':
self.mixin.delete_arp_entry_for_dvr_service_port(
self.ctx, port)
self.assertEqual(3, l3_notify.del_arp_entry.call_count)
expected_calls = [
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.11',
'mac_address': 'my_mac',
'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323'}),
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.21',
'mac_address': 'my_mac',
'subnet_id': '2b7c8a07-6f8e-4937-8701-f1d5da1a807c'}),
mock.call(self.ctx, router.id, {
'ip_address': 'fd45:1515:7e0:0:f816:3eff:fe1a:1111',
'mac_address': 'my_mac',
'subnet_id': '48534187-f077-4e81-93ff-81ec4cc0ad3b'})]
if action == 'update':
l3_notify.add_arp_entry.assert_has_calls(expected_calls)
elif action == 'delete':
l3_notify.del_arp_entry.assert_has_calls(expected_calls)
def test_update_arp_entry_for_dvr_service_port_no_aap(self):
self._test_arp_entry_for_dvr_service_port_no_aap(action='update')
def test_delete_arp_entry_for_dvr_service_port_no_aap(self):
self._test_arp_entry_for_dvr_service_port_no_aap(action='delete')
def _test_arp_entry_for_dvr_service_port_aap(self, action):
router_dict = {'name': 'test_router', 'admin_state_up': True,
'distributed': True}
router = self._create_router(router_dict)
plugin = mock.Mock()
directory.add_plugin(plugin_constants.CORE, plugin)
l3_notify = self.mixin.l3_rpc_notifier = mock.Mock()
port = {
'id': 'my_port_id',
'network_id': 'my_network_id',
'fixed_ips': [
{'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323',
'ip_address': '10.0.0.11'}],
'mac_address': 'my_mac',
'device_owner': 'nova:compute',
'allowed_address_pairs': [
{'ip_address': '10.0.0.12',
'mac_address': 'aa:bb:cc:dd:ee:ff'},
{'ip_address': '10.0.0.13'},
{'ip_address': '10.0.0.0/24'},
{'ip_address': '10.0.0.14/32',
'mac_address': 'aa:bb:cc:dd:ee:ff'}
]
}
dvr_port = {
'id': 'dvr_port_id',
'fixed_ips': mock.ANY,
'device_owner': const.DEVICE_OWNER_DVR_INTERFACE,
'device_id': router['id']
}
plugin.get_ports.return_value = [dvr_port]
subnet = {'id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323',
'cidr': '10.0.0.0/24'}
with mock.patch.object(self.mixin._core_plugin, 'get_subnets',
return_value=[subnet]):
if action == 'update':
self.mixin.update_arp_entry_for_dvr_service_port(
self.ctx, port)
elif action == 'delete':
self.mixin.delete_arp_entry_for_dvr_service_port(
self.ctx, port)
expected_calls = [
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.11',
'mac_address': 'my_mac',
'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323'}
),
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.12',
'mac_address': 'aa:bb:cc:dd:ee:ff',
'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323'}),
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.13',
'mac_address': 'my_mac',
'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323'}),
mock.call(self.ctx, router.id, {
'ip_address': '10.0.0.14',
'mac_address': 'aa:bb:cc:dd:ee:ff',
'subnet_id': '51edc9e0-24f9-47f2-8e1e-2a41cb691323'})]
if action == 'update':
l3_notify.add_arp_entry.assert_has_calls(expected_calls)
elif action == 'delete':
l3_notify.del_arp_entry.assert_has_calls(expected_calls)
def test_update_arp_entry_for_dvr_service_port_aap(self):
self._test_arp_entry_for_dvr_service_port_aap(action='update')
def test_delete_arp_entry_for_dvr_service_port_aap(self):
self._test_arp_entry_for_dvr_service_port_aap(action='delete')
def test_add_router_interface_csnat_ports_failure(self):
router_dict = {'name': 'test_router', 'admin_state_up': True,

View File

@ -0,0 +1,8 @@
---
fixes:
- |
An issue when arp entry in DVR router for allowed address pairs (AAP) is
configured for parent port MAC address even when AAP has different MAC address.
Ensure we use MAC address from AAP if it is set and fallback to parent
port mac address.