b99dee2df6
Previously we assumed that we can look up the resource provider (created by nova) to be used as the parent of the agent and physical NIC resource provider tree by the name set in the config option DEFAULT.host. This assumption was wrong. While nova-compute's DEFAULT.host and neutron-agent's DEFAULT.host must match for port binding to work, the root resource provider created by nova does not belong to the compute host (where nova-compute runs) but it belongs to the compute nodes (i.e. hypervisors). Actually there may be multiple compute nodes managed by a single nova-compute (think of ironic). Plus the value of DEFAULT.host and the compute node's ID may be different even when nova-compute manages a hypervisor on the same host because of various deployment considerations. For example when tripleo does not manage the undercloud (so a libvirt hypervisor returns the plain hostname), but the same tripleo enforces it's host naming conventions in nova's and neutron's DEFAULT.host settings. This change enables neutron to use the hypervisor name to locate the root of the resource provider tree. We introduce a new configuration option for (1) ovs-agent: resource_provider_hypervisors, for example: [ovs] bridge_mappings = physnet0:br-physnet0,... resource_provider_bandwidths = br-physnet0:10000000:10000000,... resource_provider_hypervisors = br-physnet0:hypervisor0,... (2) sriov-agent: resource_provider_hypervisors, for example: [sriov_nic] bridge_mappings = physnet1:ens5,... resource_provider_bandwidths = ens5:10000000:10000000,... resource_provider_hypervisors = ens5:hypervisor1,... For both agents 'resource_provider_hypervisors' values default to socket.gethostname() for each key in resource_provider_bandwidths. We try to not block later developments in which one neutron agent may manage devices on multiple hosts. That's why we allow the each physdev to be associated with a different hypervisor. But here we do not try to solve the problem that the natural physdev identifiers may not be unique accross multiple hosts. We leave solving this problem to whoever wants to implement an agent handling devices of multiple hosts. (3) We extend report_state message's configurations field alike: { 'bridge_mappings': {'physnet0': 'br-physnet0'}, 'resource_provider_bandwidths': { 'br-physnet0': {'egress': 10000000, 'ingress': 10000000}}, 'resource_provider_hypervisors': {'br-physnet0': 'hypervisor0'}, ... } (4) In neutron-server we use report_state.configurations.resource_provider_hypervisors.PHYSDEV when selecting parent resource provider for agent and physdev RP-tree. When not available in the message we fall back to using report_state.host as before. Since we only changed the free-format configurations field of the report_state message rpc version is not bumped and we expect this change to be backported to stein and train. Removed unapplicable TODO notes from backport. Conflicts: neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py Change-Id: I9b08a3a9c20b702b745b41d4885fb5120fd665ce Closes-Bug: #1853840 (cherry picked from commit258eebea71
) (cherry picked from commit9a6766470e
)
227 lines
8.7 KiB
Python
227 lines
8.7 KiB
Python
# Copyright 2018 Ericsson
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import uuid
|
|
|
|
import mock
|
|
|
|
from neutron.agent.common import placement_report
|
|
from neutron.tests import base
|
|
|
|
|
|
class DeferredCallTestCase(base.BaseTestCase):
|
|
|
|
def test_defer_not_called(self):
|
|
func = mock.Mock()
|
|
placement_report.DeferredCall(func)
|
|
func.assert_not_called()
|
|
|
|
def test_execute(self):
|
|
func = mock.Mock()
|
|
deferred = placement_report.DeferredCall(
|
|
func, 'some arg', kwarg='some kwarg')
|
|
deferred.execute()
|
|
func.assert_called_once_with('some arg', kwarg='some kwarg')
|
|
|
|
def test___str__(self):
|
|
def func():
|
|
pass
|
|
deferred = placement_report.DeferredCall(func, 42, foo='bar')
|
|
self.assertEqual("func(42, foo='bar')", str(deferred))
|
|
|
|
|
|
class PlacementStateTestCase(base.BaseTestCase):
|
|
|
|
def setUp(self):
|
|
super(PlacementStateTestCase, self).setUp()
|
|
self.client_mock = mock.Mock()
|
|
self.driver_uuid_namespace = uuid.UUID(
|
|
'00000000-0000-0000-0000-000000000001')
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost'
|
|
self.hypervisor1_rp_uuid = uuid.UUID(
|
|
'c0b4abe5-516f-54b8-b965-ff94060dcbcc')
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost2'
|
|
self.hypervisor2_rp_uuid = uuid.UUID(
|
|
'544155b7-1295-5f10-b5f0-eadc50abc6d4')
|
|
self.kwargs = {
|
|
'rp_bandwidths': {},
|
|
'rp_inventory_defaults': {},
|
|
'driver_uuid_namespace': self.driver_uuid_namespace,
|
|
'agent_type': 'fake agent type',
|
|
'hypervisor_rps': {
|
|
'eth0': {'name': 'fakehost', 'uuid': self.hypervisor1_rp_uuid},
|
|
'eth1': {'name': 'fakehost', 'uuid': self.hypervisor1_rp_uuid},
|
|
},
|
|
'device_mappings': {},
|
|
'supported_vnic_types': [],
|
|
'client': self.client_mock,
|
|
}
|
|
|
|
def test__deferred_update_physnet_traits(self):
|
|
self.kwargs.update({
|
|
'device_mappings': {
|
|
'physnet0': ['eth0'],
|
|
'physnet1': ['eth1'],
|
|
},
|
|
'rp_bandwidths': {
|
|
'eth0': {'egress': 1, 'ingress': 1},
|
|
},
|
|
})
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state._deferred_update_physnet_traits():
|
|
deferred.execute()
|
|
|
|
self.client_mock.update_trait.assert_called_with(
|
|
name='CUSTOM_PHYSNET_PHYSNET0')
|
|
|
|
def test__deferred_update_vnic_type_traits(self):
|
|
self.kwargs.update({
|
|
'supported_vnic_types': ['direct'],
|
|
})
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state._deferred_update_vnic_type_traits():
|
|
deferred.execute()
|
|
|
|
self.client_mock.update_trait.assert_any_call(
|
|
name='CUSTOM_VNIC_TYPE_DIRECT')
|
|
|
|
def test__deferred_create_agent_rps(self):
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state._deferred_create_agent_rps():
|
|
deferred.execute()
|
|
|
|
self.client_mock.ensure_resource_provider.assert_called_with(
|
|
resource_provider={
|
|
'name': 'fakehost:fake agent type',
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost'
|
|
'uuid': uuid.UUID('c0b4abe5-516f-54b8-b965-ff94060dcbcc'),
|
|
'parent_provider_uuid': self.hypervisor1_rp_uuid})
|
|
|
|
def test__deferred_create_agent_rps_multiple_hypervisors(self):
|
|
self.kwargs['hypervisor_rps']['eth1'] = {
|
|
'name': 'fakehost2',
|
|
'uuid': self.hypervisor2_rp_uuid,
|
|
}
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state._deferred_create_agent_rps():
|
|
deferred.execute()
|
|
|
|
self.client_mock.ensure_resource_provider.assert_has_calls(
|
|
any_order=True,
|
|
calls=[
|
|
mock.call(resource_provider={
|
|
'name': 'fakehost:fake agent type',
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' \
|
|
# 'fakehost'
|
|
'uuid': uuid.UUID('c0b4abe5-516f-54b8-b965-ff94060dcbcc'),
|
|
'parent_provider_uuid': self.hypervisor1_rp_uuid}),
|
|
mock.call(resource_provider={
|
|
'name': 'fakehost2:fake agent type',
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' \
|
|
# 'fakehost2'
|
|
'uuid': uuid.UUID('544155b7-1295-5f10-b5f0-eadc50abc6d4'),
|
|
'parent_provider_uuid': self.hypervisor2_rp_uuid}),
|
|
]
|
|
)
|
|
|
|
def test_deferred_create_resource_providers(self):
|
|
self.kwargs.update({
|
|
'rp_bandwidths': {
|
|
'eth0': {'egress': 1, 'ingress': 1},
|
|
},
|
|
})
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state.deferred_create_resource_providers():
|
|
deferred.execute()
|
|
|
|
self.client_mock.ensure_resource_provider.assert_called_with(
|
|
{'name': 'fakehost:fake agent type:eth0',
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001'
|
|
# 'fakehost:eth0'
|
|
'uuid': uuid.UUID('1ea6f823-bcf2-5dc5-9bee-4ee6177a6451'),
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost'
|
|
'parent_provider_uuid': uuid.UUID(
|
|
'c0b4abe5-516f-54b8-b965-ff94060dcbcc')})
|
|
|
|
def test_deferred_update_resource_provider_traits(self):
|
|
self.kwargs.update({
|
|
'device_mappings': {
|
|
'physnet0': ['eth0'],
|
|
},
|
|
'rp_bandwidths': {
|
|
'eth0': {'egress': 1, 'ingress': 1},
|
|
},
|
|
'supported_vnic_types': ['normal'],
|
|
})
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state.deferred_update_resource_provider_traits():
|
|
deferred.execute()
|
|
|
|
self.client_mock.update_resource_provider_traits.assert_called()
|
|
self.assertEqual(
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' 'fakehost:eth0'
|
|
uuid.UUID('1ea6f823-bcf2-5dc5-9bee-4ee6177a6451'),
|
|
self.client_mock.update_resource_provider_traits.call_args[1][
|
|
'resource_provider_uuid'])
|
|
# NOTE(bence romsics): To avoid testing the _order_ of traits.
|
|
self.assertEqual(
|
|
set(['CUSTOM_PHYSNET_PHYSNET0', 'CUSTOM_VNIC_TYPE_NORMAL']),
|
|
set(self.client_mock.update_resource_provider_traits.call_args[1][
|
|
'traits']))
|
|
|
|
def test_deferred_update_resource_provider_inventories(self):
|
|
self.kwargs.update({
|
|
'device_mappings': {
|
|
'physnet0': ['eth0'],
|
|
},
|
|
'rp_bandwidths': {
|
|
'eth0': {'egress': 100, 'ingress': None},
|
|
},
|
|
'rp_inventory_defaults': {
|
|
'step_size': 10,
|
|
'max_unit': 50,
|
|
},
|
|
})
|
|
state = placement_report.PlacementState(**self.kwargs)
|
|
|
|
for deferred in state.deferred_update_resource_provider_inventories():
|
|
deferred.execute()
|
|
|
|
self.client_mock.\
|
|
update_resource_provider_inventories.assert_called_with(
|
|
# uuid below generated by the following command:
|
|
# uuid -v5 '00000000-0000-0000-0000-000000000001' \
|
|
# 'fakehost:eth0'
|
|
resource_provider_uuid=uuid.UUID(
|
|
'1ea6f823-bcf2-5dc5-9bee-4ee6177a6451'),
|
|
inventories={
|
|
'NET_BW_EGR_KILOBIT_PER_SEC': {
|
|
'total': 100,
|
|
'step_size': 10,
|
|
'max_unit': 50}})
|