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:
parent
fcc8081df7
commit
d343dcb7ca
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
|
@ -25,6 +25,7 @@ SUPPORTED_VERSIONS = [
|
|||
'1.7',
|
||||
'1.8',
|
||||
'1.9',
|
||||
'1.10',
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue