192 lines
8.2 KiB
Python
192 lines
8.2 KiB
Python
# 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'
|
|
|
|
|
|
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>',
|
|
dest='resources',
|
|
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 VCPU=4 --resource DISK_GB=64 '
|
|
'--resource MEMORY_MB=2048``'
|
|
)
|
|
parser.add_argument(
|
|
'--limit',
|
|
metavar='<limit>',
|
|
help='A positive integer to limit '
|
|
'the maximum number of allocation candidates. '
|
|
'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``.'
|
|
)
|
|
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=[],
|
|
action='append',
|
|
metavar='<aggregate_uuid>',
|
|
help='UUID of the resource provider aggregate of which the '
|
|
'returned allocation candidates are a member. The returned '
|
|
'allocation candidates must be associated with at least one '
|
|
'of the aggregates identified by uuid. '
|
|
'May be repeated.\n\n'
|
|
'This param requires at least '
|
|
'``--os-placement-api-version 1.21``.'
|
|
)
|
|
|
|
return parser
|
|
|
|
@version.check(version.ge('1.10'))
|
|
def take_action(self, parsed_args):
|
|
if not parsed_args.resources:
|
|
raise exceptions.CommandError(
|
|
'At least one --resource must be specified.')
|
|
|
|
for resource in parsed_args.resources:
|
|
if not len(resource.split('=')) == 2:
|
|
raise exceptions.CommandError(
|
|
'Arguments to --resource must be of form '
|
|
'<resource_class>=<value>')
|
|
|
|
http = self.app.client_manager.placement
|
|
|
|
params = {'resources': ','.join(
|
|
resource.replace('=', ':') for resource in parsed_args.resources)}
|
|
if 'limit' in parsed_args and parsed_args.limit:
|
|
# 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)
|
|
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'))
|
|
params['member_of'] = 'in:' + ','.join(parsed_args.aggregate_uuid)
|
|
|
|
resp = http.request('GET', BASE_URL, params=params).json()
|
|
|
|
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():
|
|
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')):
|
|
for i, allocation_req in enumerate(resp['allocation_requests']):
|
|
for rp, resources in allocation_req['allocations'].items():
|
|
req = ','.join(
|
|
'%s=%s' % (rc, value)
|
|
for rc, value in resources['resources'].items())
|
|
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']:
|
|
rp = allocation['resource_provider']['uuid']
|
|
req = ','.join(
|
|
'%s=%s' % (rc, value)
|
|
for rc, value in allocation['resources'].items())
|
|
rows.append([i + 1, req, rp, rp_resources[rp]])
|
|
|
|
fields = ('#', 'allocation', 'resource provider',
|
|
'inventory used/capacity')
|
|
if include_traits:
|
|
fields += ('traits',)
|
|
|
|
return fields, rows
|