Add support for 1.22 microversion

The 1.22 microversion of placement adds support for
excluding resource providers and allocation candidates
with specified traits. A forbidden trait may be specified
with --forbidden option.

Change-Id: I4760fe6119e29d80f63ec7bd6c4a5f2395a9291e
This commit is contained in:
Pawel Baclawski 2019-04-02 13:21:21 +00:00
parent 4c95f7caff
commit e52fa969f9
7 changed files with 151 additions and 2 deletions

View File

@ -86,6 +86,16 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
'This option requires at least '
'``--os-placement-api-version 1.17``.'
)
parser.add_argument(
'--forbidden',
metavar='<forbidden>',
action='append',
default=[],
help='A forbidden trait. May be repeated. Returned allocation '
'candidates must not contain any of the specified traits. '
'This option requires at least '
'``--os-placement-api-version 1.22``.'
)
parser.add_argument(
'--aggregate-uuid',
default=[],
@ -126,6 +136,14 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
# Fail if --required but not high enough microversion.
self.check_version(version.ge('1.17'))
params['required'] = ','.join(parsed_args.required)
if 'forbidden' in parsed_args and parsed_args.forbidden:
self.check_version(version.ge('1.22'))
forbidden_traits = ','.join(
['!' + f for f in parsed_args.forbidden])
if 'required' in params:
params['required'] += ',' + forbidden_traits
else:
params['required'] = forbidden_traits
if 'aggregate_uuid' in parsed_args and parsed_args.aggregate_uuid:
# Fail if --aggregate_uuid but not high enough microversion.
self.check_version(version.ge('1.21'))

View File

@ -128,6 +128,16 @@ class ListResourceProvider(command.Lister, version.CheckerMixin):
'This option requires at least '
'``--os-placement-api-version 1.18``.'
)
parser.add_argument(
'--forbidden',
metavar='<forbidden>',
action='append',
default=[],
help='A forbidden trait. May be repeated. Returned resource '
'providers must not contain any of the specified traits. '
'This option requires at least '
'``--os-placement-api-version 1.22``.'
)
return parser
@ -153,6 +163,14 @@ class ListResourceProvider(command.Lister, version.CheckerMixin):
if 'required' in parsed_args and parsed_args.required:
self.check_version(version.ge('1.18'))
filters['required'] = ','.join(parsed_args.required)
if 'forbidden' in parsed_args and parsed_args.forbidden:
self.check_version(version.ge('1.22'))
forbidden_traits = ','.join(
['!' + f for f in parsed_args.forbidden])
if 'required' in filters:
filters['required'] += ',' + forbidden_traits
else:
filters['required'] = forbidden_traits
url = common.url_with_filters(BASE_URL, filters)
resources = http.request('GET', url).json()['resource_providers']

View File

@ -189,7 +189,7 @@ class BaseTestCase(base.BaseTestCase):
def resource_provider_list(self, uuid=None, name=None,
aggregate_uuids=None, resources=None,
in_tree=None, required=None):
in_tree=None, required=None, forbidden=None):
to_exec = 'resource provider list'
if uuid:
to_exec += ' --uuid ' + uuid
@ -204,6 +204,8 @@ class BaseTestCase(base.BaseTestCase):
to_exec += ' --in-tree ' + in_tree
if required:
to_exec += ' ' + ' '.join('--required %s' % t for t in required)
if forbidden:
to_exec += ' ' + ' '.join('--forbidden %s' % f for f in forbidden)
return self.openstack(to_exec, use_json=True)
@ -354,12 +356,15 @@ class BaseTestCase(base.BaseTestCase):
cmd = 'resource provider trait delete %s ' % uuid
self.openstack(cmd)
def allocation_candidate_list(self, resources, required=None, limit=None,
def allocation_candidate_list(self, resources, required=None,
forbidden=None, limit=None,
aggregate_uuids=None):
cmd = 'allocation candidate list ' + ' '.join(
'--resource %s' % resource for resource in resources)
if required is not None:
cmd += ''.join([' --required %s' % t for t in required])
if forbidden:
cmd += ' ' + ' '.join('--forbidden %s' % f for f in forbidden)
if limit is not None:
cmd += ' --limit %d' % limit
if aggregate_uuids:

View File

@ -188,3 +188,52 @@ class TestAllocationCandidate121(base.BaseTestCase):
self.assertEqual(1, len(candidate_dict))
self.assertIn(rp2['uuid'], candidate_dict)
self.assertNotIn(rp1['uuid'], candidate_dict)
# Specifying forbidden traits weren't available until version 1.22
def test_fail_if_forbidden_trait_wrong_version(self):
self.assertCommandFailed(
'Operation or argument is not supported with version 1.21',
self.allocation_candidate_list,
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
forbidden=('STORAGE_DISK_HDD',))
class TestAllocationCandidate122(base.BaseTestCase):
VERSION = '1.22'
def test_hide_forbidden_trait(self):
rp1 = self.resource_provider_create()
rp2 = self.resource_provider_create()
rp3 = self.resource_provider_create()
self.resource_inventory_set(
rp1['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_inventory_set(
rp2['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_inventory_set(
rp3['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_provider_trait_set(
rp1['uuid'], 'STORAGE_DISK_SSD', 'HW_CPU_X86_BMI')
self.resource_provider_trait_set(
rp2['uuid'], 'STORAGE_DISK_HDD', 'HW_CPU_X86_BMI')
self.resource_provider_trait_set(
rp3['uuid'], 'STORAGE_DISK_HDD', 'HW_CPU_X86_BMI')
rps = self.allocation_candidate_list(
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
required=('HW_CPU_X86_BMI',),
forbidden=('STORAGE_DISK_HDD',))
self.assertEqual(1, len(rps))
self.assertEqual(rp1['uuid'], rps[0]['resource provider'])
rps = self.allocation_candidate_list(
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
required=('HW_CPU_X86_BMI',),
forbidden=('STORAGE_DISK_SSD',))
uuids = [rp['resource provider'] for rp in rps]
self.assertEqual(2, len(uuids))
self.assertNotIn(rp1['uuid'], uuids)
self.assertIn(rp2['uuid'], uuids)
self.assertIn(rp3['uuid'], uuids)

View File

@ -303,3 +303,52 @@ class TestResourceProvider118(base.BaseTestCase):
self.assertEqual(1, len(rps))
self.assertEqual(rp2['uuid'], rps[0]['uuid'])
# Specifying forbidden traits weren't available until version 1.22
def test_fail_if_forbidden_trait_wrong_version(self):
self.assertCommandFailed(
'Operation or argument is not supported with version 1.18',
self.resource_provider_list,
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
forbidden=('STORAGE_DISK_HDD',))
class TestResourceProvider122(base.BaseTestCase):
VERSION = '1.22'
def test_hide_forbidden_trait(self):
rp1 = self.resource_provider_create()
rp2 = self.resource_provider_create()
rp3 = self.resource_provider_create()
self.resource_inventory_set(
rp1['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_inventory_set(
rp2['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_inventory_set(
rp3['uuid'], 'MEMORY_MB=1024', 'DISK_GB=256')
self.resource_provider_trait_set(
rp1['uuid'], 'STORAGE_DISK_SSD', 'HW_CPU_X86_VMX')
self.resource_provider_trait_set(
rp2['uuid'], 'STORAGE_DISK_HDD', 'HW_CPU_X86_VMX')
self.resource_provider_trait_set(
rp3['uuid'], 'STORAGE_DISK_HDD', 'HW_CPU_X86_VMX')
rps = self.resource_provider_list(
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
required=('HW_CPU_X86_VMX',),
forbidden=('STORAGE_DISK_HDD',))
self.assertEqual(1, len(rps))
self.assertEqual(rp1['uuid'], rps[0]['uuid'])
rps = self.resource_provider_list(
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
required=('HW_CPU_X86_VMX',),
forbidden=('!STORAGE_DISK_SSD',))
uuids = [rp['uuid'] for rp in rps]
self.assertEqual(2, len(uuids))
self.assertNotIn(rp1['uuid'], uuids)
self.assertIn(rp2['uuid'], uuids)
self.assertIn(rp3['uuid'], uuids)

View File

@ -37,6 +37,7 @@ SUPPORTED_VERSIONS = [
'1.19',
'1.20', # unused
'1.21',
'1.22',
]

View File

@ -0,0 +1,9 @@
---
features:
- |
The `1.22 microversion`_ of placement adds support for
excluding resource providers and allocation candidates
with specified traits. A forbidden trait may be specified
with ``--forbidden`` option.
.. _1.22 microversion: https://docs.openstack.org/placement/latest/placement-api-microversion-history.html#support-forbidden-traits-on-resource-providers-and-allocations-candidates