Allocation candidates parameter: required (v1.17)

New parameter to 'allocation candidate list':
    --required TRAIT [--required ...]

New output field of 'allocation candidate list':
    traits

Change-Id: Ie70497981ff4295b3ccd6b495b21bbb919d92c47
Partially-Implements: blueprint placement-osc-plugin-rocky
This commit is contained in:
Bence Romsics 2018-02-27 11:25:14 +01:00 committed by Matt Riedemann
parent 9f4e7eb9e8
commit 5883b82f69
5 changed files with 73 additions and 8 deletions

View File

@ -17,7 +17,6 @@ from osc_placement import version
BASE_URL = '/allocation_candidates'
FIELDS = ('#', 'allocation', 'resource provider', 'inventory used/capacity')
class ListAllocationCandidate(command.Lister, version.CheckerMixin):
@ -76,6 +75,16 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
'This option requires at least '
'``--os-placement-api-version 1.16``.'
)
parser.add_argument(
'--required',
metavar='<required>',
action='append',
default=[],
help='A required trait. May be repeated. Allocation candidates '
'must collectively contain all of the required traits. '
'This option requires at least '
'``--os-placement-api-version 1.17``.'
)
return parser
@ -93,13 +102,22 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
# Fail if --limit but not high enough microversion.
self.check_version(version.ge('1.16'))
params['limit'] = int(parsed_args.limit)
if 'required' in parsed_args and parsed_args.required:
# Fail if --required but not high enough microversion.
self.check_version(version.ge('1.17'))
params['required'] = ','.join(parsed_args.required)
resp = http.request('GET', BASE_URL, params=params).json()
rps = {}
rp_resources = {}
include_traits = self.compare_version(version.ge('1.17'))
if include_traits:
rp_traits = {}
for rp_uuid, resources in resp['provider_summaries'].items():
rps[rp_uuid] = ','.join(
rp_resources[rp_uuid] = ','.join(
'%s=%s/%s' % (rc, value['used'], value['capacity'])
for rc, value in resources['resources'].items())
if include_traits:
rp_traits[rp_uuid] = ','.join(resources['traits'])
rows = []
if self.compare_version(version.ge('1.12')):
@ -108,7 +126,11 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
req = ','.join(
'%s=%s' % (rc, value)
for rc, value in resources['resources'].items())
rows.append([i + 1, req, rp, rps[rp]])
if include_traits:
row = [i + 1, req, rp, rp_resources[rp], rp_traits[rp]]
else:
row = [i + 1, req, rp, rp_resources[rp]]
rows.append(row)
else:
for i, allocation_req in enumerate(resp['allocation_requests']):
for allocation in allocation_req['allocations']:
@ -116,6 +138,11 @@ class ListAllocationCandidate(command.Lister, version.CheckerMixin):
req = ','.join(
'%s=%s' % (rc, value)
for rc, value in allocation['resources'].items())
rows.append([i + 1, req, rp, rps[rp]])
rows.append([i + 1, req, rp, rp_resources[rp]])
return FIELDS, rows
fields = ('#', 'allocation', 'resource provider',
'inventory used/capacity')
if include_traits:
fields += ('traits',)
return fields, rows

View File

@ -258,9 +258,11 @@ class BaseTestCase(base.BaseTestCase):
cmd = 'resource provider trait delete %s ' % uuid
self.openstack(cmd)
def allocation_candidate_list(self, resources, limit=None):
def allocation_candidate_list(self, resources, required=None, limit=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 limit is not None:
cmd += ' --limit %d' % limit
return self.openstack(cmd, use_json=True)

View File

@ -118,3 +118,30 @@ class TestAllocationCandidate116(base.BaseTestCase):
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
limit=1)
self.assertEqual(len(set([row['#'] for row in limited])), 1)
class TestAllocationCandidate117(base.BaseTestCase):
VERSION = '1.17'
def test_show_required_trait(self):
rp1 = self.resource_provider_create()
rp2 = self.resource_provider_create()
self.resource_inventory_set(
rp1['uuid'], 'MEMORY_MB=8192', 'DISK_GB=512')
self.resource_inventory_set(
rp2['uuid'], 'MEMORY_MB=8192', 'DISK_GB=512')
self.resource_provider_trait_set(
rp1['uuid'], 'STORAGE_DISK_SSD', 'HW_NIC_SRIOV')
self.resource_provider_trait_set(
rp2['uuid'], 'STORAGE_DISK_HDD', 'HW_NIC_SRIOV')
rps = self.allocation_candidate_list(
resources=('MEMORY_MB=1024', 'DISK_GB=80'),
required=('STORAGE_DISK_SSD',))
candidate_dict = {rp['resource provider']: rp for rp in rps}
self.assertIn(rp1['uuid'], candidate_dict)
self.assertNotIn(rp2['uuid'], candidate_dict)
self.assertEqual(
set(candidate_dict[rp1['uuid']]['traits'].split(',')),
set(['STORAGE_DISK_SSD', 'HW_NIC_SRIOV']))

View File

@ -31,7 +31,8 @@ SUPPORTED_VERSIONS = [
'1.13', # unused
'1.14',
'1.15', # unused
'1.16'
'1.16',
'1.17'
]

View File

@ -0,0 +1,8 @@
---
features:
- |
Support is added for the `1.17`_ placement API microversion by adding
the ``--required`` option to the ``openstack allocation candidate list``
command.
.. _1.17: https://docs.openstack.org/nova/latest/user/placement.html#add-required-parameter-to-the-allocation-candidates-maximum-in-queens