diff --git a/neutron/agent/common/placement_report.py b/neutron/agent/common/placement_report.py index 103a23b949b..dab95e91089 100644 --- a/neutron/agent/common/placement_report.py +++ b/neutron/agent/common/placement_report.py @@ -205,14 +205,30 @@ class PlacementState(object): return agent_rp_traits def deferred_update_resource_provider_traits(self): + + def _get_traits(device, physical_bridges, physnet_trait_mappings): + if device == self._rp_tun_name and device not in physical_bridges: + # That means the RP for tunnelled networks is not associated + # to a physical bridge interface. + return [n_const.TRAIT_NETWORK_TUNNEL] + elif device == self._rp_tun_name and device in physical_bridges: + # The physical network and the tunnelled networks share the + # same physical interface. + return [n_const.TRAIT_NETWORK_TUNNEL, + physnet_trait_mappings[device]] + else: + # Just the physical interface. + return [physnet_trait_mappings.get(device)] + rp_traits = [] - tunnelled_trait_mappings = { - self._rp_tun_name: n_const.TRAIT_NETWORK_TUNNEL} + physical_bridges = {br for brs in self._device_mappings.values() for + br in brs} physnet_trait_mappings = {} for physnet, devices in self._device_mappings.items(): for device in devices: physnet_trait_mappings[device] = place_utils.physnet_trait( physnet) + vnic_type_traits = [place_utils.vnic_type_trait(vnic_type) for vnic_type in self._supported_vnic_types] @@ -221,8 +237,8 @@ class PlacementState(object): self._driver_uuid_namespace, self._hypervisor_rps[device]['name'], device) - traits = [physnet_trait_mappings.get(device) or - tunnelled_trait_mappings[device]] + traits = _get_traits(device, physical_bridges, + physnet_trait_mappings) traits.extend(vnic_type_traits) rp_traits.append( DeferredCall( diff --git a/neutron/tests/unit/agent/common/test_placement_report.py b/neutron/tests/unit/agent/common/test_placement_report.py index 26c41de20b5..8b108b971df 100644 --- a/neutron/tests/unit/agent/common/test_placement_report.py +++ b/neutron/tests/unit/agent/common/test_placement_report.py @@ -15,6 +15,8 @@ from unittest import mock import uuid +from oslo_config import cfg + from neutron.agent.common import placement_report from neutron.common import _constants as n_const from neutron.conf.plugins.ml2 import config as ml2_config @@ -243,6 +245,48 @@ class PlacementStateTestCase(base.BaseTestCase): {'CUSTOM_VNIC_TYPE_NORMAL'}], actual_traits) + def test_deferred_update_resource_provider_traits_shared_rp(self): + import uuid + self.kwargs.update({ + 'device_mappings': { + 'physnet0': ['eth0'], + }, + 'rp_bandwidths': { + 'eth0': {'egress': 1, 'ingress': 1}, + }, + 'supported_vnic_types': ['normal'], + }) + cfg.CONF.set_override('tunnelled_network_rp_name', 'eth0', group='ml2') + state = placement_report.PlacementState(**self.kwargs) + + for deferred in state.deferred_update_resource_provider_traits(): + deferred.execute() + + expected_calls = [ + # uuid below generated by the following command: + # uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost:eth0' + mock.call( + resource_provider_uuid=uuid.UUID( + '1ea6f823-bcf2-5dc5-9bee-4ee6177a6451'), + traits=mock.ANY), + + # uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost' + mock.call( + resource_provider_uuid=uuid.UUID( + 'c0b4abe5-516f-54b8-b965-ff94060dcbcc'), + traits=mock.ANY)] + self.client_mock.update_resource_provider_traits.assert_has_calls( + expected_calls) + + # NOTE(bence romsics): To avoid testing the _order_ of traits. + actual_traits = [set(args[1]['traits']) for args in + self.client_mock.update_resource_provider_traits.call_args_list] + self.assertEqual( + [{n_const.TRAIT_NETWORK_TUNNEL, 'CUSTOM_PHYSNET_PHYSNET0', + 'CUSTOM_VNIC_TYPE_NORMAL'}, + {'CUSTOM_VNIC_TYPE_NORMAL'}], + actual_traits) + def test_deferred_update_resource_provider_inventories_bw(self): self.kwargs.update({ 'device_mappings': {