CLI allocation candidates (v1.10)

Adds the "openstack allocation candidate list" command.

Change-Id: I5508243771e20a9ab7724731a4289b218594dee5
Partially-Implements: blueprint placement-osc-plugin-rocky
This commit is contained in:
Andrey Volkov 2017-10-23 16:35:59 +03:00 committed by Matt Riedemann
parent fcc8081df7
commit d343dcb7ca
6 changed files with 210 additions and 0 deletions

View File

@ -0,0 +1,100 @@
# 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.
from osc_lib.command import command
from osc_lib import exceptions
from osc_placement import version
BASE_URL = '/allocation_candidates'
FIELDS = ('#', 'allocation', 'resource provider', 'inventory used/capacity')
class ListAllocationCandidate(command.Lister, version.CheckerMixin):
"""List allocation candidates.
Returns a representation of a collection of allocation requests and
resource provider summaries. Each allocation request has information
to issue an "openstack resource provider allocation set" request to claim
resources against a related set of resource providers.
As several allocation requests are available its necessary to select one.
To make a decision, resource provider summaries are provided with the
inventory/capacity information.
For example::
$ export OS_PLACEMENT_API_VERSION=1.10
$ openstack allocation candidate list --resource VCPU=1
+---+------------+-------------------------+-------------------------+
| # | allocation | resource provider | inventory used/capacity |
+---+------------+-------------------------+-------------------------+
| 1 | VCPU=1 | 66bcaca9-9263-45b1-a569 | VCPU=0/128 |
| | | -ea708ff7a968 | |
+---+------------+-------------------------+-------------------------+
In this case, the user is looking for resource providers that can have
capacity to allocate 1 VCPU resource class. There is one resource provider
that can serve that allocation request and that resource providers current
VCPU inventory used is 0 and available capacity is 128.
This command requires at least ``--os-placement-api-version 1.10``.
"""
def get_parser(self, prog_name):
parser = super(ListAllocationCandidate, self).get_parser(prog_name)
parser.add_argument(
'--resource',
metavar='<resource_class>=<value>',
action='append',
default=[],
help='String indicating an amount of resource of a specified '
'class that providers in each allocation request must '
'collectively have the capacity and availability to serve. '
'Can be specified multiple times per resource class. '
'For example: '
'``--resource VCP=4 --resource DISK_GB=64 '
'--resource MEMORY_MB=2048``'
)
return parser
@version.check(version.ge('1.10'))
def take_action(self, parsed_args):
if not parsed_args.resource:
raise exceptions.CommandError(
'At least one --resource must be specified.')
http = self.app.client_manager.placement
params = {'resources': ','.join(
resource.replace('=', ':') for resource in parsed_args.resource)}
resp = http.request('GET', BASE_URL, params=params).json()
rps = {}
for rp_uuid, resources in resp['provider_summaries'].items():
rps[rp_uuid] = ','.join(
'%s=%s/%s' % (rc, value['used'], value['capacity'])
for rc, value in resources['resources'].items())
rows = []
for i, allocation_req in enumerate(resp['allocation_requests']):
for allocation in allocation_req['allocations']:
rp = allocation['resource_provider']['uuid']
req = ','.join(
'%s=%s' % (rc, value)
for rc, value in allocation['resources'].items())
rows.append([i + 1, req, rp, rps[rp]])
return FIELDS, rows

View File

@ -248,3 +248,8 @@ class BaseTestCase(base.BaseTestCase):
def resource_provider_trait_delete(self, uuid):
cmd = 'resource provider trait delete %s ' % uuid
self.openstack(cmd)
def allocation_candidate_list(self, *resources):
cmd = 'allocation candidate list ' + ' '.join(
'--resource %s' % resource for resource in resources)
return self.openstack(cmd, use_json=True)

View File

@ -0,0 +1,92 @@
# 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
from osc_placement.tests.functional import base
def sorted_resources(resource):
return ','.join(sorted(resource.split(',')))
class TestAllocationCandidate(base.BaseTestCase):
VERSION = '1.10'
def test_list_no_resource_specified_error(self):
self.assertCommandFailed(
'At least one --resource must be specified',
self.openstack, 'allocation candidate list')
def test_list_empty(self):
self.assertEqual([], self.allocation_candidate_list(
'MEMORY_MB=999999999'))
def test_list_one(self):
rp = self.resource_provider_create()
self.resource_inventory_set(rp['uuid'], 'MEMORY_MB=1024')
candidates = self.allocation_candidate_list('MEMORY_MB=256')
self.assertIn(
rp['uuid'],
[candidate['resource provider'] for candidate in candidates])
def assertResourceEqual(self, r1, r2):
self.assertEqual(sorted_resources(r1), sorted_resources(r2))
def test_list_multiple(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=16384', 'DISK_GB=1024')
candidates = self.allocation_candidate_list(
'MEMORY_MB=1024', 'DISK_GB=80')
rps = {c['resource provider']: c for c in candidates}
self.assertResourceEqual(
'MEMORY_MB=1024,DISK_GB=80', rps[rp1['uuid']]['allocation'])
self.assertResourceEqual(
'MEMORY_MB=1024,DISK_GB=80', rps[rp2['uuid']]['allocation'])
self.assertResourceEqual(
'MEMORY_MB=0/8192,DISK_GB=0/512',
rps[rp1['uuid']]['inventory used/capacity'])
self.assertResourceEqual(
'MEMORY_MB=0/16384,DISK_GB=0/1024',
rps[rp2['uuid']]['inventory used/capacity'])
def test_list_shared(self):
rp1 = self.resource_provider_create()
rp2 = self.resource_provider_create()
self.resource_inventory_set(rp1['uuid'], 'MEMORY_MB=8192')
self.resource_inventory_set(rp2['uuid'], 'DISK_GB=1024')
agg = str(uuid.uuid4())
self.resource_provider_aggregate_set(rp1['uuid'], agg)
self.resource_provider_aggregate_set(rp2['uuid'], agg)
self.resource_provider_trait_set(
rp2['uuid'], 'MISC_SHARES_VIA_AGGREGATE')
candidates = self.allocation_candidate_list(
'MEMORY_MB=1024', 'DISK_GB=80')
rps = {c['resource provider']: c for c in candidates}
self.assertResourceEqual(
'MEMORY_MB=1024', rps[rp1['uuid']]['allocation'])
self.assertResourceEqual(
'DISK_GB=80', rps[rp2['uuid']]['allocation'])
self.assertResourceEqual(
'MEMORY_MB=0/8192', rps[rp1['uuid']]['inventory used/capacity'])
self.assertResourceEqual(
'DISK_GB=0/1024', rps[rp2['uuid']]['inventory used/capacity'])
self.assertEqual(
rps[rp2['uuid']]['#'], rps[rp1['uuid']]['#'])
def test_fail_if_unknown_rc(self):
self.assertCommandFailed(
'No such resource', self.allocation_candidate_list, 'UNKNOWN=10')

View File

@ -25,6 +25,7 @@ SUPPORTED_VERSIONS = [
'1.7',
'1.8',
'1.9',
'1.10',
]

View File

@ -0,0 +1,11 @@
---
features:
- |
The ``openstack allocation candidate list`` command is
available starting from microversion `1.10`_.
See the command documentation for `allocation candidate list`_ for
more details.
.. _1.10: https://docs.openstack.org/nova/latest/user/placement.html#allocation-candidates-maximum-in-pike
.. _allocation candidate list: https://docs.openstack.org/osc-placement/latest/cli/index.html#allocation-candidate-list

View File

@ -56,6 +56,7 @@ openstack.placement.v1 =
resource_provider_trait_list = osc_placement.resources.trait:ListResourceProviderTrait
resource_provider_trait_set = osc_placement.resources.trait:SetResourceProviderTrait
resource_provider_trait_delete = osc_placement.resources.trait:DeleteResourceProviderTrait
allocation_candidate_list = osc_placement.resources.allocation_candidate:ListAllocationCandidate
[build_sphinx]
source-dir = doc/source