osc-placement/osc_placement/resources/allocation_candidate.py

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