Merge "[DVR] Send allowed address pairs info to the L3 agents" into stable/queens

This commit is contained in:
Zuul 2021-06-09 11:40:04 +00:00 committed by Gerrit Code Review
commit d6bfd8385b
7 changed files with 63 additions and 13 deletions

View File

@ -320,6 +320,11 @@ class DvrLocalRouter(dvr_router_base.DvrRouterBase):
p['mac_address'], p['mac_address'],
subnet_id, subnet_id,
'add') 'add')
for allowed_address_pair in p.get('allowed_address_pairs', []):
self._update_arp_entry(allowed_address_pair['ip_address'],
allowed_address_pair['mac_address'],
subnet_id,
'add')
self._process_arp_cache_for_internal_port(subnet_id) self._process_arp_cache_for_internal_port(subnet_id)
@staticmethod @staticmethod

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
import collections
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import port as port_def from neutron_lib.api.definitions import port as port_def
@ -65,6 +66,17 @@ class AllowedAddressPairsMixin(object):
return [self._make_allowed_address_pairs_dict(pair.db_obj) return [self._make_allowed_address_pairs_dict(pair.db_obj)
for pair in pairs] for pair in pairs]
def get_allowed_address_pairs_for_ports(self, context, port_ids):
pairs = (
obj_addr_pair.AllowedAddressPair.
get_allowed_address_pairs_for_ports(
context, port_ids=port_ids))
result = collections.defaultdict(list)
for pair in pairs:
result[pair.port_id].append(
self._make_allowed_address_pairs_dict(pair.db_obj))
return result
@staticmethod @staticmethod
@resource_extend.extends([port_def.COLLECTION_NAME]) @resource_extend.extends([port_def.COLLECTION_NAME])
def _extend_port_dict_allowed_address_pairs(port_res, port_db): def _extend_port_dict_allowed_address_pairs(port_res, port_db):

View File

@ -1251,11 +1251,23 @@ class L3_NAT_with_dvr_db_mixin(_DVRAgentInterfaceMixin,
def get_ports_under_dvr_connected_subnet(self, context, subnet_id): def get_ports_under_dvr_connected_subnet(self, context, subnet_id):
query = dvr_mac_db.get_ports_query_by_subnet_and_ip(context, subnet_id) query = dvr_mac_db.get_ports_query_by_subnet_and_ip(context, subnet_id)
ports = [p for p in query.all() if is_port_bound(p)] ports = [p for p in query.all() if is_port_bound(p)]
return [ # TODO(slaweq): if there would be way to pass to neutron-lib only
# list of extensions which actually should be processed, than setting
# process_extensions=True below could avoid that second loop and
# getting allowed_address_pairs for each of the ports
ports_list = [
self.l3plugin._core_plugin._make_port_dict( self.l3plugin._core_plugin._make_port_dict(
port, process_extensions=False) port, process_extensions=False)
for port in ports for port in ports
] ]
ports_ids = [p['id'] for p in ports_list]
allowed_address_pairs = (
self._core_plugin.get_allowed_address_pairs_for_ports(
context, ports_ids))
for port in ports_list:
port['allowed_address_pairs'] = allowed_address_pairs.get(
port['id'], [])
return ports_list
def is_distributed_router(router): def is_distributed_router(router):

View File

@ -10,8 +10,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from neutron.common import utils from neutron.common import utils
from neutron.db import api as db_api
from neutron.db.models import allowed_address_pair as models from neutron.db.models import allowed_address_pair as models
from neutron.objects import base from neutron.objects import base
from neutron.objects import common_types from neutron.objects import common_types
@ -61,3 +61,12 @@ class AllowedAddressPair(base.NeutronDbObject):
fields['mac_address'] = utils.AuthenticEUI( fields['mac_address'] = utils.AuthenticEUI(
fields['mac_address']) fields['mac_address'])
return fields return fields
@classmethod
def get_allowed_address_pairs_for_ports(cls, context, port_ids):
with db_api.context_manager.reader.using(context):
query = context.session.query(models.AllowedAddressPair).filter(
models.AllowedAddressPair.port_id.in_(port_ids))
pairs = [cls._load_object(context, db_obj)
for db_obj in query.all()]
return pairs

View File

@ -1018,21 +1018,25 @@ class TestDvrRouter(framework.L3AgentTestFramework):
# cache is properly populated. # cache is properly populated.
self.agent.conf.agent_mode = 'dvr_snat' self.agent.conf.agent_mode = 'dvr_snat'
router_info = self.generate_dvr_router_info(enable_snat=True) router_info = self.generate_dvr_router_info(enable_snat=True)
expected_neighbor = '35.4.1.10' expected_neighbors = ['35.4.1.10', '10.0.0.10']
port_data = { port_data = {
'fixed_ips': [{'ip_address': expected_neighbor}], 'fixed_ips': [{'ip_address': expected_neighbors[0]}],
'mac_address': 'fa:3e:aa:bb:cc:dd', 'mac_address': 'fa:3e:aa:bb:cc:dd',
'device_owner': DEVICE_OWNER_COMPUTE 'device_owner': DEVICE_OWNER_COMPUTE,
'allowed_address_pairs': [
{'ip_address': expected_neighbors[1],
'mac_address': 'fa:3e:aa:bb:cc:dd'}]
} }
self.agent.plugin_rpc.get_ports_by_subnet.return_value = [port_data] self.agent.plugin_rpc.get_ports_by_subnet.return_value = [port_data]
router1 = self.manage_router(self.agent, router_info) router1 = self.manage_router(self.agent, router_info)
internal_device = router1.get_internal_device_name( internal_device = router1.get_internal_device_name(
router_info['_interfaces'][0]['id']) router_info['_interfaces'][0]['id'])
neighbor = ip_lib.dump_neigh_entries(4, internal_device, for expected_neighbor in expected_neighbors:
router1.ns_name, neighbor = ip_lib.dump_neigh_entries(4, internal_device,
dst=expected_neighbor) router1.ns_name,
self.assertNotEqual([], neighbor) dst=expected_neighbor)
self.assertEqual(expected_neighbor, neighbor[0]['dst']) self.assertNotEqual([], neighbor)
self.assertEqual(expected_neighbor, neighbor[0]['dst'])
def _assert_rfp_fpr_mtu(self, router, expected_mtu=1500): def _assert_rfp_fpr_mtu(self, router, expected_mtu=1500):
dev_mtu = self.get_device_mtu( dev_mtu = self.get_device_mtu(

View File

@ -532,7 +532,10 @@ class TestDvrRouterOperations(base.BaseTestCase):
'device_owner': lib_constants.DEVICE_OWNER_DHCP, 'device_owner': lib_constants.DEVICE_OWNER_DHCP,
'fixed_ips': [{'ip_address': '1.2.3.4', 'fixed_ips': [{'ip_address': '1.2.3.4',
'prefixlen': 24, 'prefixlen': 24,
'subnet_id': subnet_id}]}, 'subnet_id': subnet_id}],
'allowed_address_pairs': [
{'ip_address': '10.20.30.40',
'mac_address': '00:11:22:33:44:55'}]},
{'mac_address': '11:22:33:44:55:66', {'mac_address': '11:22:33:44:55:66',
'device_owner': lib_constants.DEVICE_OWNER_LOADBALANCER, 'device_owner': lib_constants.DEVICE_OWNER_LOADBALANCER,
'fixed_ips': [{'ip_address': '1.2.3.5', 'fixed_ips': [{'ip_address': '1.2.3.5',
@ -554,8 +557,9 @@ class TestDvrRouterOperations(base.BaseTestCase):
'_process_arp_cache_for_internal_port') as parp: '_process_arp_cache_for_internal_port') as parp:
ri._set_subnet_arp_info(subnet_id) ri._set_subnet_arp_info(subnet_id)
self.assertEqual(1, parp.call_count) self.assertEqual(1, parp.call_count)
self.mock_ip_dev.neigh.add.assert_called_once_with( self.mock_ip_dev.neigh.add.assert_has_calls([
'1.2.3.4', '00:11:22:33:44:55') mock.call('1.2.3.4', '00:11:22:33:44:55'),
mock.call('10.20.30.40', '00:11:22:33:44:55')])
# Test negative case # Test negative case
router['distributed'] = False router['distributed'] = False

View File

@ -1077,6 +1077,8 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
router = self._create_router(router_dict) router = self._create_router(router_dict)
with self.network() as network,\ with self.network() as network,\
self.subnet(network=network) as subnet: self.subnet(network=network) as subnet:
self.mixin._core_plugin.get_allowed_address_pairs_for_ports = (
mock.Mock())
fake_bound_ports_ids = [] fake_bound_ports_ids = []
def fake_is_port_bound(port): def fake_is_port_bound(port):
@ -1098,6 +1100,8 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase):
self.ctx, subnet['subnet']['id']) self.ctx, subnet['subnet']['id'])
dvr_subnet_ports_ids = [p['id'] for p in dvr_subnet_ports] dvr_subnet_ports_ids = [p['id'] for p in dvr_subnet_ports]
self.assertItemsEqual(fake_bound_ports_ids, dvr_subnet_ports_ids) self.assertItemsEqual(fake_bound_ports_ids, dvr_subnet_ports_ids)
(self.mixin._core_plugin.get_allowed_address_pairs_for_ports.
assert_called_once_with(self.ctx, dvr_subnet_ports_ids))
@mock.patch.object(l3_db, 'can_port_be_bound_to_virtual_bridge', @mock.patch.object(l3_db, 'can_port_be_bound_to_virtual_bridge',
return_value=True) return_value=True)