Make update_qos_minbw_allocation() more generic
update_qos_minbw_allocation() function is used to update Placement allocation. Initially, only minimum bandwidth rule allocated resources in Placement, but with the introduction of minimum packet rate rule that's changed. Because of that, we should rename the function to make it more generic and to avoid confusion. The second problem with this function is that it allows to update resources only of a single RP at a time, even if multiple RPs are associated with the same consumer UUID. With introduction of a minimum packet rate rule it makes sense to allow to update resources of multiple RPs in a single API call. To accommodate this, we need to slightly modify arguments this function takes, and embed RP UUID in alloc_diff, rather than pass it as a separate parameter. Addresses TODO comment from [1]. [1] https://opendev.org/openstack/neutron/src/branch/master/neutron/services/qos/qos_plugin.py#L68 Partial-Bug: #1943724 Related-Bug: #1922237 See-Also: https://review.opendev.org/785236 Change-Id: Ie28b95e8ed351ab88db1fc75c83a02c474582e0b
This commit is contained in:
parent
b35ba2fe95
commit
d2bd7760ef
@ -752,39 +752,40 @@ class PlacementAPIClient(object):
|
||||
url = '/allocations/%s' % consumer_uuid
|
||||
return self._get(url).json()
|
||||
|
||||
def update_qos_minbw_allocation(self, consumer_uuid, minbw_alloc_diff,
|
||||
rp_uuid):
|
||||
def update_qos_allocation(self, consumer_uuid, alloc_diff):
|
||||
"""Update allocation for QoS minimum bandwidth consumer
|
||||
|
||||
:param consumer_uuid: The uuid of the consumer, in case of bound port
|
||||
owned by a VM, the VM uuid.
|
||||
:param minbw_alloc_diff: A dict which contains the fields to update
|
||||
for the allocation under the given resource
|
||||
provider.
|
||||
:param rp_uuid: uuid of the resource provider for which the
|
||||
allocations are to be updated.
|
||||
:param alloc_diff: A dict which contains RP UUIDs as keys and
|
||||
corresponding fields to update for the allocation
|
||||
under the given resource provider.
|
||||
"""
|
||||
for i in range(GENERATION_CONFLICT_RETRIES):
|
||||
body = self.list_allocations(consumer_uuid)
|
||||
if not body['allocations']:
|
||||
raise n_exc.PlacementAllocationRemoved(consumer=consumer_uuid)
|
||||
if rp_uuid not in body['allocations']:
|
||||
raise n_exc.PlacementAllocationRpNotExists(
|
||||
resource_provider=rp_uuid, consumer=consumer_uuid)
|
||||
# Count new min_kbps values based on the diff in alloc_diff
|
||||
for drctn, min_kbps_diff in minbw_alloc_diff.items():
|
||||
orig_kbps = body['allocations'][rp_uuid]['resources'][drctn]
|
||||
new_kbps = orig_kbps + min_kbps_diff
|
||||
if new_kbps > 0:
|
||||
body['allocations'][rp_uuid]['resources'][drctn] = new_kbps
|
||||
else:
|
||||
# Remove the resource class if the new value for min_kbps
|
||||
# is 0
|
||||
resources = body['allocations'][rp_uuid]['resources']
|
||||
if len(resources) > 1:
|
||||
resources.pop(drctn, None)
|
||||
# Count new values based on the diff in alloc_diff
|
||||
for rp_uuid, diff in alloc_diff.items():
|
||||
if rp_uuid not in body['allocations']:
|
||||
raise n_exc.PlacementAllocationRpNotExists(
|
||||
resource_provider=rp_uuid, consumer=consumer_uuid)
|
||||
for drctn, value in diff.items():
|
||||
orig_value = body['allocations'][rp_uuid][
|
||||
'resources'].get(drctn, 0)
|
||||
new_value = orig_value + value
|
||||
if new_value > 0:
|
||||
body['allocations'][rp_uuid]['resources'][
|
||||
drctn] = new_value
|
||||
else:
|
||||
body['allocations'].pop(rp_uuid)
|
||||
# Remove the resource class if the new value is 0
|
||||
resources = body['allocations'][rp_uuid]['resources']
|
||||
resources.pop(drctn, None)
|
||||
|
||||
# Remove RPs without any resources
|
||||
body['allocations'] = {
|
||||
rp: alloc for rp, alloc in body['allocations'].items()
|
||||
if alloc.get('resources')}
|
||||
try:
|
||||
# Update allocations has no return body, but leave the loop
|
||||
return self.update_allocation(consumer_uuid, body)
|
||||
|
@ -26,6 +26,7 @@ from neutron_lib.tests import _base as base
|
||||
|
||||
|
||||
RESOURCE_PROVIDER_UUID = uuidutils.generate_uuid()
|
||||
SECOND_RESOURCE_PROVIDER_UUID = uuidutils.generate_uuid()
|
||||
CONSUMER_UUID = uuidutils.generate_uuid()
|
||||
RESOURCE_PROVIDER_NAME = 'resource_provider_name'
|
||||
RESOURCE_PROVIDER = {
|
||||
@ -727,23 +728,20 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
}}
|
||||
)
|
||||
|
||||
def _get_allocation_response(self, resources):
|
||||
def _get_allocation_response(self, allocation):
|
||||
mock_rsp_get = mock.Mock()
|
||||
mock_rsp_get.json = lambda: {
|
||||
'allocations': {
|
||||
RESOURCE_PROVIDER_UUID: resources
|
||||
}
|
||||
'allocations': allocation
|
||||
}
|
||||
return mock_rsp_get
|
||||
|
||||
def test_update_qos_minbw_allocation(self):
|
||||
def test_update_qos_allocation(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{'resources': {'a': 3, 'b': 2}})
|
||||
{RESOURCE_PROVIDER_UUID: {'resources': {'a': 3, 'b': 2}}})
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp_get]
|
||||
self.placement_api_client.update_qos_minbw_allocation(
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={'a': 2, 'b': 2},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': 2, 'b': 2}},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % CONSUMER_UUID,
|
||||
@ -753,32 +751,31 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
}}
|
||||
)
|
||||
|
||||
def test_update_qos_minbw_allocation_removed(self):
|
||||
def test_update_qos_allocation_removed(self):
|
||||
mock_rsp = mock.Mock()
|
||||
mock_rsp.json = lambda: {'allocations': {}}
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp]
|
||||
self.assertRaises(
|
||||
n_exc.PlacementAllocationRemoved,
|
||||
self.placement_api_client.update_qos_minbw_allocation,
|
||||
self.placement_api_client.update_qos_allocation,
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={'a': 1, 'b': 1},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': 1, 'b': 1}},
|
||||
)
|
||||
|
||||
def test_update_qos_minbw_allocation_rp_not_exists(self):
|
||||
def test_update_qos_allocation_rp_not_exists(self):
|
||||
mock_rsp = mock.Mock()
|
||||
mock_rsp.json = lambda: {'allocations': {'other:rp:uuid': {'c': 3}}}
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp]
|
||||
self.assertRaises(
|
||||
n_exc.PlacementAllocationRpNotExists,
|
||||
self.placement_api_client.update_qos_minbw_allocation,
|
||||
self.placement_api_client.update_qos_allocation,
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={'a': 1, 'b': 1},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': 1, 'b': 1}},
|
||||
)
|
||||
|
||||
def test_update_qos_minbw_allocation_max_retries(self):
|
||||
mock_rsp_get = self._get_allocation_response({'c': 3})
|
||||
def test_update_qos_allocation_max_retries(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{RESOURCE_PROVIDER_UUID: {'c': 3}})
|
||||
self.placement_fixture.mock_get.side_effect = 10 * [mock_rsp_get]
|
||||
mock_rsp_put = mock.Mock()
|
||||
mock_rsp_put.json = lambda: {
|
||||
@ -787,15 +784,15 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
response=mock_rsp_put)
|
||||
self.assertRaises(
|
||||
n_exc.PlacementAllocationGenerationConflict,
|
||||
self.placement_api_client.update_qos_minbw_allocation,
|
||||
self.placement_api_client.update_qos_allocation,
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID,
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {}},
|
||||
)
|
||||
self.assertEqual(10, self.placement_fixture.mock_put.call_count)
|
||||
|
||||
def test_update_qos_minbwallocation_generation_conflict_solved(self):
|
||||
mock_rsp_get = self._get_allocation_response({'c': 3})
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{RESOURCE_PROVIDER_UUID: {'c': 3}})
|
||||
self.placement_fixture.mock_get.side_effect = 2 * [mock_rsp_get]
|
||||
mock_rsp_put = mock.Mock()
|
||||
mock_rsp_put.json = lambda: {
|
||||
@ -804,15 +801,15 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
ks_exc.Conflict(response=mock_rsp_put),
|
||||
mock.Mock()
|
||||
]
|
||||
self.placement_api_client.update_qos_minbw_allocation(
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {}},
|
||||
)
|
||||
self.assertEqual(2, self.placement_fixture.mock_put.call_count)
|
||||
|
||||
def test_update_qos_minbw_allocation_other_conflict(self):
|
||||
mock_rsp_get = self._get_allocation_response({'c': 3})
|
||||
def test_update_qos_allocation_other_conflict(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{RESOURCE_PROVIDER_UUID: {'c': 3}})
|
||||
self.placement_fixture.mock_get.side_effect = 10*[mock_rsp_get]
|
||||
mock_rsp_put = mock.Mock()
|
||||
mock_rsp_put.text = ''
|
||||
@ -822,34 +819,31 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
response=mock_rsp_put)
|
||||
self.assertRaises(
|
||||
ks_exc.Conflict,
|
||||
self.placement_api_client.update_qos_minbw_allocation,
|
||||
self.placement_api_client.update_qos_allocation,
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID,
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {}},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once()
|
||||
|
||||
def test_update_qos_minbw_allocation_to_zero(self):
|
||||
def test_update_qos_allocation_to_zero(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{'resources': {'a': 3, 'b': 2}})
|
||||
{RESOURCE_PROVIDER_UUID: {'resources': {'a': 3, 'b': 2}}})
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp_get]
|
||||
self.placement_api_client.update_qos_minbw_allocation(
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={'a': -3, 'b': -2},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': -3, 'b': -2}},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % CONSUMER_UUID,
|
||||
{'allocations': {}})
|
||||
|
||||
def test_update_qos_minbw_allocation_one_class_to_zero(self):
|
||||
def test_update_qos_allocation_one_class_to_zero(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{'resources': {'a': 3, 'b': 2}})
|
||||
{RESOURCE_PROVIDER_UUID: {'resources': {'a': 3, 'b': 2}}})
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp_get]
|
||||
self.placement_api_client.update_qos_minbw_allocation(
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
minbw_alloc_diff={'a': -3, 'b': 1},
|
||||
rp_uuid=RESOURCE_PROVIDER_UUID
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': -3, 'b': 1}},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % CONSUMER_UUID,
|
||||
@ -857,3 +851,41 @@ class TestPlacementAPIClient(base.BaseTestCase):
|
||||
RESOURCE_PROVIDER_UUID: {
|
||||
'resources': {'b': 3}}
|
||||
}})
|
||||
|
||||
def test_update_qos_allocation_one_class_to_zero_and_new_class(self):
|
||||
mock_rsp_get = self._get_allocation_response(
|
||||
{RESOURCE_PROVIDER_UUID: {'resources': {'a': 3}}})
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp_get]
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
alloc_diff={RESOURCE_PROVIDER_UUID: {'a': -3, 'b': 1}},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % CONSUMER_UUID,
|
||||
{'allocations': {
|
||||
RESOURCE_PROVIDER_UUID: {
|
||||
'resources': {'b': 1}}
|
||||
}})
|
||||
|
||||
def test_update_qos_allocation_multiple_rps(self):
|
||||
mock_rsp_get = self._get_allocation_response({
|
||||
RESOURCE_PROVIDER_UUID: {'resources': {'a': 3, 'b': 2}},
|
||||
SECOND_RESOURCE_PROVIDER_UUID: {'resources': {'c': 1, 'd': 5}},
|
||||
})
|
||||
self.placement_fixture.mock_get.side_effect = [mock_rsp_get]
|
||||
self.placement_api_client.update_qos_allocation(
|
||||
consumer_uuid=CONSUMER_UUID,
|
||||
alloc_diff={
|
||||
RESOURCE_PROVIDER_UUID: {'a': -3, 'b': 2},
|
||||
SECOND_RESOURCE_PROVIDER_UUID: {'e': 3, 'd': -5},
|
||||
},
|
||||
)
|
||||
self.placement_fixture.mock_put.assert_called_once_with(
|
||||
'/allocations/%s' % CONSUMER_UUID,
|
||||
{'allocations': {
|
||||
RESOURCE_PROVIDER_UUID: {
|
||||
'resources': {'b': 4}},
|
||||
SECOND_RESOURCE_PROVIDER_UUID: {
|
||||
'resources': {'c': 1, 'e': 3}},
|
||||
}}
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user