Add "--resource-class" to allocation unset

The ``openstack resource provider allocation unset`` command now
supports ``--resource-class`` option, which accepts string of a
resource class. This will remove allocations for the given resource
class from all the providers. If ``--provider`` option is also
specified, allocations to remove will be limited to the given resource
class of the given resource provider.

example1::

  # remove VGPU allocation from provider P for this consumer.
  allocation unset <consumer_uuid> --provider P --resource-class VGPU

example2::

  # remove VGPU allocations from all providers for this consumer.
  allocation unset <consumer_uuid> --resource-class VGPU

Change-Id: I91baf9de5205ec15023706a1646556850302d3d9
Story: #2006779
Task: #37304
This commit is contained in:
Tetsuro Nakamura 2019-11-25 20:38:51 +00:00
parent 05ca1ee2e1
commit a4ac717a48
4 changed files with 163 additions and 62 deletions

View File

@ -155,8 +155,8 @@ class SetAllocation(command.Lister, version.CheckerMixin):
class UnsetAllocation(command.Lister, version.CheckerMixin):
"""Removes one or more sets of provider allocations for a consumer.
Note that omitting the ``--provider`` option is equivalent to removing
all allocations for the given consumer.
Note that omitting both the ``--provider`` and the ``--resource-class``
option is equivalent to removing all allocations for the given consumer.
This command requires ``--os-placement-api-version 1.12`` or greater. Use
``openstack resource provider allocation set`` for older versions.
@ -168,7 +168,10 @@ class UnsetAllocation(command.Lister, version.CheckerMixin):
parser.add_argument(
'uuid',
metavar='<consumer_uuid>',
help='UUID of the consumer'
help='UUID of the consumer. It is strongly recommended to use '
'``--os-placement-api-version 1.28`` or greater when using '
'this option to ensure the other allocation information is '
'retained. '
)
parser.add_argument(
'--provider',
@ -180,22 +183,27 @@ class UnsetAllocation(command.Lister, version.CheckerMixin):
'consumer has allocations on more than one provider, for '
'example after evacuating a server to another compute node '
'and you want to cleanup allocations on the source compute '
'node resource provider in order to delete it. It is '
'strongly recommended to use '
'``--os-placement-api-version 1.28`` or greater when using '
'this option to ensure the other allocation information is '
'retained. Specify multiple times to remove allocations '
'against multiple resource providers. Omit this option to '
'remove all allocations for the consumer.'
'node resource provider in order to delete it. Specify '
'multiple times to remove allocations against multiple '
'resource providers. Omit this option to remove all '
'allocations for the consumer, or to remove all allocations'
'of a specific resource class from all the resource provider '
'with the ``--resource_class`` option. '
)
parser.add_argument(
'--resource-class',
metavar='resource_class',
action='append',
default=[],
help='Name of a resource class from which to remove allocations '
'for the given consumer. This is useful when the consumer '
'has allocations on more than one resource class. '
'By default, this will remove allocations for the given '
'resource class from all the providers. If ``--provider`` '
'option is also specified, allocations to remove will be '
'limited to that resource class of the given resource '
'provider.'
)
# TODO(mriedem): Add a --resource-class option which can be used with
# or without --provider, e.g.:
# 1. allocation unset --provider P --resource-class VGPU = remove
# VGPU allocation from provider P for this consumer.
# 2. allocation unset --resource-class VGPU = remove VGPU allocations
# from all providers for this consumer.
# Make sure to update the note about omitting --provider in the
# command description above (due to example 2).
return parser
# NOTE(mriedem): We require >= 1.12 because PUT requires project_id/user_id
@ -210,19 +218,35 @@ class UnsetAllocation(command.Lister, version.CheckerMixin):
# Get the current allocations.
payload = http.request('GET', url).json()
allocations = payload['allocations']
if parsed_args.provider:
allocations = payload['allocations']
# Remove the given provider(s) from the allocations if it exists.
# Do not error out if the consumer does not have allocations
# against a provider in case we lost a race since the allocations
# are in the state the user wants them in anyway.
for rp_uuid in parsed_args.provider:
allocations.pop(rp_uuid, None)
if parsed_args.resource_class:
# Remove the given resource class. Do not error out if the
# consumer does not have allocations against that resource
# class.
rp_uuids = set(allocations)
if parsed_args.provider:
# If providers are also specified, we limit to remove
# allocations only from those providers
rp_uuids &= set(parsed_args.provider)
for rp_uuid in rp_uuids:
for rc in parsed_args.resource_class:
allocations[rp_uuid]['resources'].pop(rc, None)
if not allocations[rp_uuid]['resources']:
allocations.pop(rp_uuid, None)
else:
# No --provider(s) specified so remove allocations from all
# providers.
allocations = {}
if parsed_args.provider:
# Remove the given provider(s) from the allocations if it
# exists. Do not error out if the consumer does not have
# allocations against a provider in case we lost a race since
# the allocations are in the state the user wants them in
# anyway.
for rp_uuid in parsed_args.provider:
allocations.pop(rp_uuid, None)
else:
# No --provider(s) specified so remove allocations from all
# providers.
allocations = {}
supports_consumer_generation = self.compare_version(version.ge('1.28'))
# 1.28+ allows PUTing an empty allocations dict as long as a

View File

@ -260,19 +260,18 @@ class BaseTestCase(base.BaseTestCase):
return result
def resource_allocation_unset(self, consumer_uuid, provider=None,
use_json=True):
resource_class=None, use_json=True):
cmd = 'resource provider allocation unset %s' % consumer_uuid
if resource_class:
cmd += ' ' + ' '.join(
'--resource-class %s' % rc for rc in resource_class)
if provider:
# --provider can be specified multiple times so if we only get
# a single string value convert to a list.
if isinstance(provider, six.string_types):
provider = [provider]
cmd = 'resource provider allocation unset %s %s' % (
' '.join('--provider %s' %
rp_uuid for rp_uuid in provider),
consumer_uuid
)
else:
cmd = 'resource provider allocation unset %s' % consumer_uuid
cmd += ' ' + ' '.join(
'--provider %s' % rp_uuid for rp_uuid in provider)
result = self.openstack(cmd, use_json=use_json)
def cleanup(uuid):

View File

@ -22,12 +22,8 @@ class TestAllocation(base.BaseTestCase):
super(TestAllocation, self).setUp()
self.rp1 = self.resource_provider_create()
self.inv_cpu1 = self.resource_inventory_set(
self.rp1['uuid'],
'VCPU=4',
'VCPU:max_unit=4',
'MEMORY_MB=1024',
'MEMORY_MB:max_unit=1024')
self.resource_inventory_set(
self.rp1['uuid'], 'VCPU=4', 'MEMORY_MB=1024')
def test_allocation_show_not_found(self):
consumer_uuid = str(uuid.uuid4())
@ -141,12 +137,8 @@ class TestAllocation112(base.BaseTestCase):
super(TestAllocation112, self).setUp()
self.rp1 = self.resource_provider_create()
self.inv_cpu1 = self.resource_inventory_set(
self.rp1['uuid'],
'VCPU=4',
'VCPU:max_unit=4',
'MEMORY_MB=1024',
'MEMORY_MB:max_unit=1024')
self.resource_inventory_set(
self.rp1['uuid'], 'VCPU=4', 'MEMORY_MB=1024')
def test_allocation_update(self):
consumer_uuid = str(uuid.uuid4())
@ -230,32 +222,49 @@ class TestAllocationUnset112(base.BaseTestCase):
def setUp(self):
super(TestAllocationUnset112, self).setUp()
# Create two providers with inventory.
# Create four providers with inventory.
self.rp1 = self.resource_provider_create()
self.inv_cpu1 = self.resource_inventory_set(
self.rp1['uuid'],
'VCPU=4',
'VCPU:max_unit=4',
'MEMORY_MB=1024',
'MEMORY_MB:max_unit=1024')
self.rp2 = self.resource_provider_create()
self.resource_inventory_set(self.rp2['uuid'], 'VGPU=1')
# Create allocations against both providers for the same consumer.
self.consumer_uuid = str(uuid.uuid4())
self.rp3 = self.resource_provider_create()
self.rp4 = self.resource_provider_create()
self.resource_inventory_set(
self.rp1['uuid'], 'VCPU=4', 'MEMORY_MB=1024')
self.resource_inventory_set(
self.rp2['uuid'], 'VGPU=1')
self.resource_inventory_set(
self.rp3['uuid'], 'VCPU=4', 'MEMORY_MB=1024', 'VGPU=1')
self.resource_inventory_set(
self.rp4['uuid'], 'VCPU=4', 'MEMORY_MB=1024', 'VGPU=1')
self.consumer_uuid1 = str(uuid.uuid4())
self.consumer_uuid2 = str(uuid.uuid4())
self.project_uuid = str(uuid.uuid4())
self.user_uuid = str(uuid.uuid4())
# Create allocations against rp1 and rp2 for consumer1.
self.resource_allocation_set(
self.consumer_uuid,
self.consumer_uuid1,
['rp={},VCPU=2'.format(self.rp1['uuid']),
'rp={},MEMORY_MB=512'.format(self.rp1['uuid']),
'rp={},VGPU=1'.format(self.rp2['uuid'])],
project_id=self.project_uuid, user_id=self.user_uuid)
# Create allocations against rp3 and rp4 for consumer1.
self.resource_allocation_set(
self.consumer_uuid2,
['rp={},VCPU=1'.format(self.rp3['uuid']),
'rp={},MEMORY_MB=256'.format(self.rp3['uuid']),
'rp={},VGPU=1'.format(self.rp3['uuid']),
'rp={},VCPU=1'.format(self.rp4['uuid']),
'rp={},MEMORY_MB=256'.format(self.rp4['uuid'])],
project_id=self.project_uuid, user_id=self.user_uuid)
def test_allocation_unset_one_provider(self):
"""Tests removing allocations for one specific provider."""
# Remove the allocation for rp1.
updated_allocs = self.resource_allocation_unset(
self.consumer_uuid, provider=self.rp1['uuid'])
self.consumer_uuid1, provider=self.rp1['uuid'])
expected = [
{'resource_provider': self.rp2['uuid'],
'generation': 3,
@ -265,12 +274,62 @@ class TestAllocationUnset112(base.BaseTestCase):
]
self.assertEqual(expected, updated_allocs)
def test_allocation_unset_one_resource_class(self):
"""Tests removing allocations for resource classes."""
updated_allocs = self.resource_allocation_unset(
self.consumer_uuid2, resource_class=['MEMORY_MB'])
expected = [
{'resource_provider': self.rp3['uuid'],
'generation': 3,
'project_id': self.project_uuid,
'user_id': self.user_uuid,
'resources': {'VCPU': 1, 'VGPU': 1}},
{'resource_provider': self.rp4['uuid'],
'generation': 3,
'project_id': self.project_uuid,
'user_id': self.user_uuid,
'resources': {'VCPU': 1}}
]
self.assertEqual(expected, updated_allocs)
def test_allocation_unset_resource_classes(self):
"""Tests removing allocations for resource classes."""
updated_allocs = self.resource_allocation_unset(
self.consumer_uuid2, resource_class=['VCPU', 'MEMORY_MB'])
expected = [
{'resource_provider': self.rp3['uuid'],
'generation': 3,
'project_id': self.project_uuid,
'user_id': self.user_uuid,
'resources': {'VGPU': 1}}
]
self.assertEqual(expected, updated_allocs)
def test_allocation_unset_provider_and_rc(self):
"""Tests removing allocations of resource classes for a provider ."""
updated_allocs = self.resource_allocation_unset(
self.consumer_uuid2, provider=self.rp3['uuid'],
resource_class=['VCPU', 'MEMORY_MB'])
expected = [
{'resource_provider': self.rp3['uuid'],
'generation': 3,
'project_id': self.project_uuid,
'user_id': self.user_uuid,
'resources': {'VGPU': 1}},
{'resource_provider': self.rp4['uuid'],
'generation': 3,
'project_id': self.project_uuid,
'user_id': self.user_uuid,
'resources': {'VCPU': 1, 'MEMORY_MB': 256}},
]
self.assertEqual(expected, updated_allocs)
def test_allocation_unset_remove_all_providers(self):
"""Tests removing all allocations by omitting the --provider option."""
# For this test pass use_json=False to make sure we get nothing back
# in the output since there are no more allocations.
updated_allocs = self.resource_allocation_unset(
self.consumer_uuid, use_json=False)
self.consumer_uuid1, use_json=False)
self.assertEqual('', updated_allocs.strip())

View File

@ -0,0 +1,19 @@
---
features:
- |
The ``openstack resource provider allocation unset`` command now supports
``--resource-class`` option, which accepts string of a resource class.
This will remove allocations for the given resource class from all the
providers. If ``--provider`` option is also specified, allocations to
remove will be limited to the given resource class of the given resource
provider.
example1::
# remove VGPU allocation from provider P for this consumer.
allocation unset <consumer_uuid> --provider P --resource-class VGPU
example2::
# remove VGPU allocations from all providers for this consumer.
allocation unset <consumer_uuid> --resource-class VGPU