Usages per project and user (v1.8, v1.9)
Adds support to "openstack resource provider allocation set" for microversion 1.8 where --project-id and --user-id are required (and highly recommended). Adds a new "openstack resource usage show" command for microversion 1.9 and GET /usages. Change-Id: I8ec19c8138611a06b41ec3ad949f45b8f6d7ec17 Partially-Implements: blueprint placement-osc-plugin-rocky
This commit is contained in:
parent
06b5738a3a
commit
fcc8081df7
|
@ -14,6 +14,8 @@ from osc_lib.command import command
|
|||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
|
||||
from osc_placement import version
|
||||
|
||||
|
||||
BASE_URL = '/allocations'
|
||||
FIELDS = ('generation', 'resources')
|
||||
|
@ -43,12 +45,18 @@ def parse_allocations(allocation_strings):
|
|||
return allocations
|
||||
|
||||
|
||||
class SetAllocation(command.Lister):
|
||||
"""Replaces the set of resource allocation(s) for a given consumer
|
||||
class SetAllocation(command.Lister, version.CheckerMixin):
|
||||
"""Replaces the set of resource allocation(s) for a given consumer.
|
||||
|
||||
Note that this is a full replacement of the existing allocations. If you
|
||||
want to retain the existing allocations and add a new resource class
|
||||
allocation, you must specify all resource class allocations, old and new.
|
||||
|
||||
From ``--os-placement-api-version 1.8`` it is required to specify
|
||||
``--project-id`` and ``--user-id`` to set allocations. It is highly
|
||||
recommended to provide a ``--project-id`` and ``--user-id`` when setting
|
||||
allocations for accounting and data consistency reasons.
|
||||
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
|
@ -68,7 +76,22 @@ class SetAllocation(command.Lister):
|
|||
help='Create (or update) an allocation of a resource class. '
|
||||
'Specify option multiple times to set multiple allocations.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--project-id',
|
||||
metavar='project_id',
|
||||
help='ID of the consuming project. '
|
||||
'This option is required starting from '
|
||||
'``--os-placement-api-version 1.8``.',
|
||||
required=self.compare_version(version.ge('1.8'))
|
||||
)
|
||||
parser.add_argument(
|
||||
'--user-id',
|
||||
metavar='user_id',
|
||||
help='ID of the consuming user. '
|
||||
'This option is required starting from '
|
||||
'``--os-placement-api-version 1.8``.',
|
||||
required=self.compare_version(version.ge('1.8'))
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
@ -83,7 +106,15 @@ class SetAllocation(command.Lister):
|
|||
for rp, resources in allocations.items()]
|
||||
|
||||
url = BASE_URL + '/' + parsed_args.uuid
|
||||
http.request('PUT', url, json={'allocations': allocations})
|
||||
payload = {'allocations': allocations}
|
||||
if self.compare_version(version.ge('1.8')):
|
||||
payload['project_id'] = parsed_args.project_id
|
||||
payload['user_id'] = parsed_args.user_id
|
||||
elif parsed_args.project_id or parsed_args.user_id:
|
||||
self.log.warning('--project-id and --user-id options do not '
|
||||
'affect allocation for '
|
||||
'--os-placement-api-version less than 1.8')
|
||||
http.request('PUT', url, json=payload)
|
||||
per_provider = http.request('GET', url).json()['allocations'].items()
|
||||
allocs = [dict(resource_provider=k, **v) for k, v in per_provider]
|
||||
|
||||
|
@ -93,7 +124,7 @@ class SetAllocation(command.Lister):
|
|||
|
||||
|
||||
class ShowAllocation(command.Lister):
|
||||
"""Show resource allocations for a given consumer"""
|
||||
"""Show resource allocations for a given consumer."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowAllocation, self).get_parser(prog_name)
|
||||
|
@ -119,7 +150,7 @@ class ShowAllocation(command.Lister):
|
|||
|
||||
|
||||
class DeleteAllocation(command.Command):
|
||||
"""Delete a resource allocation for a given consumer"""
|
||||
"""Delete a resource allocation for a given consumer."""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteAllocation, self).get_parser(prog_name)
|
||||
|
|
|
@ -13,11 +13,58 @@
|
|||
from osc_lib.command import command
|
||||
from osc_lib import utils
|
||||
|
||||
from osc_placement import version
|
||||
|
||||
|
||||
BASE_URL = '/resource_providers/{uuid}/usages'
|
||||
USAGES_URL = '/usages'
|
||||
FIELDS = ('resource_class', 'usage')
|
||||
|
||||
|
||||
class ResourceShowUsage(command.Lister, version.CheckerMixin):
|
||||
"""Show resource usages for a project (and optionally user) per class.
|
||||
|
||||
Gives a report of usage information for resources associated with the
|
||||
project identified by the ``project_id`` argument and user identified by
|
||||
the ``--user-id`` option.
|
||||
|
||||
This command requires at least ``--os-placement-api-version 1.9``.
|
||||
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ResourceShowUsage, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'project_id',
|
||||
metavar='<project-uuid>',
|
||||
help='ID of the project.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--user-id',
|
||||
metavar='<user-uuid>',
|
||||
help='ID of the user.'
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@version.check(version.ge('1.9'))
|
||||
def take_action(self, parsed_args):
|
||||
http = self.app.client_manager.placement
|
||||
|
||||
url = USAGES_URL
|
||||
params = {'project_id': parsed_args.project_id}
|
||||
if parsed_args.user_id:
|
||||
params['user_id'] = parsed_args.user_id
|
||||
per_class = http.request(
|
||||
'GET', url, params=params).json()['usages']
|
||||
|
||||
usages = [{'resource_class': k, 'usage': v}
|
||||
for k, v in per_class.items()]
|
||||
rows = (utils.get_dict_properties(u, FIELDS) for u in usages)
|
||||
return FIELDS, rows
|
||||
|
||||
|
||||
class ShowUsage(command.Lister):
|
||||
"""Show resource usages per class for a given resource provider."""
|
||||
|
||||
|
|
|
@ -115,12 +115,18 @@ class BaseTestCase(base.BaseTestCase):
|
|||
use_json=True
|
||||
)
|
||||
|
||||
def resource_allocation_set(self, consumer_uuid, allocations):
|
||||
def resource_allocation_set(self, consumer_uuid, allocations,
|
||||
project_id=None, user_id=None,
|
||||
use_json=True):
|
||||
cmd = 'resource provider allocation set {allocs} {uuid}'.format(
|
||||
uuid=consumer_uuid,
|
||||
allocs=' '.join('--allocation {}'.format(a) for a in allocations)
|
||||
)
|
||||
result = self.openstack(cmd, use_json=True)
|
||||
if project_id:
|
||||
cmd += ' --project-id %s' % project_id
|
||||
if user_id:
|
||||
cmd += ' --user-id %s' % user_id
|
||||
result = self.openstack(cmd, use_json=use_json)
|
||||
|
||||
def cleanup(uuid):
|
||||
try:
|
||||
|
@ -169,6 +175,12 @@ class BaseTestCase(base.BaseTestCase):
|
|||
return self.openstack('resource provider usage show ' + uuid,
|
||||
use_json=True)
|
||||
|
||||
def resource_show_usage(self, project_id, user_id=None):
|
||||
cmd = 'resource usage show %s' % project_id
|
||||
if user_id:
|
||||
cmd += ' --user-id %s' % user_id
|
||||
return self.openstack(cmd, use_json=True)
|
||||
|
||||
def resource_provider_aggregate_list(self, uuid):
|
||||
return self.openstack('resource provider aggregate list ' + uuid,
|
||||
use_json=True)
|
||||
|
|
|
@ -52,6 +52,19 @@ class TestAllocation(base.BaseTestCase):
|
|||
self.assertEqual(expected, created_alloc)
|
||||
self.assertEqual(expected, retrieved_alloc)
|
||||
|
||||
# Test that specifying --project-id and --user-id before microversion
|
||||
# 1.8 does not result in an error (they will be ignored). We have
|
||||
# to specify use_json=False because there will be a warning in the
|
||||
# output which can't be json-decoded.
|
||||
output = self.resource_allocation_set(
|
||||
consumer_uuid,
|
||||
['rp={},VCPU=2'.format(self.rp1['uuid']),
|
||||
'rp={},MEMORY_MB=512'.format(self.rp1['uuid'])],
|
||||
project_id='fake-project', user_id='fake-user', use_json=False)
|
||||
self.assertIn(
|
||||
'--project-id and --user-id options do not affect allocation for '
|
||||
'--os-placement-api-version less than 1.8', output)
|
||||
|
||||
def test_allocation_create_empty(self):
|
||||
consumer_uuid = str(uuid.uuid4())
|
||||
|
||||
|
@ -81,3 +94,35 @@ class TestAllocation(base.BaseTestCase):
|
|||
exc = self.assertRaises(subprocess.CalledProcessError,
|
||||
self.resource_allocation_delete, consumer_uuid)
|
||||
self.assertIn(msg, exc.output.decode('utf-8'))
|
||||
|
||||
|
||||
class TestAllocation18(base.BaseTestCase):
|
||||
VERSION = '1.8'
|
||||
|
||||
def test_allocation_create(self):
|
||||
consumer_uuid = str(uuid.uuid4())
|
||||
project_id = str(uuid.uuid4())
|
||||
user_id = str(uuid.uuid4())
|
||||
|
||||
rp1 = self.resource_provider_create()
|
||||
self.resource_inventory_set(
|
||||
rp1['uuid'],
|
||||
'VCPU=4',
|
||||
'VCPU:max_unit=4',
|
||||
'MEMORY_MB=1024',
|
||||
'MEMORY_MB:max_unit=1024')
|
||||
created_alloc = self.resource_allocation_set(
|
||||
consumer_uuid,
|
||||
['rp={},VCPU=2'.format(rp1['uuid']),
|
||||
'rp={},MEMORY_MB=512'.format(rp1['uuid'])],
|
||||
project_id=project_id, user_id=user_id
|
||||
)
|
||||
retrieved_alloc = self.resource_allocation_show(consumer_uuid)
|
||||
|
||||
expected = [
|
||||
{'resource_provider': rp1['uuid'],
|
||||
'generation': 2,
|
||||
'resources': {'VCPU': 2, 'MEMORY_MB': 512}}
|
||||
]
|
||||
self.assertEqual(expected, created_alloc)
|
||||
self.assertEqual(expected, retrieved_alloc)
|
||||
|
|
|
@ -58,3 +58,39 @@ class TestUsage(base.BaseTestCase):
|
|||
rp = self.resource_provider_create()
|
||||
|
||||
self.assertEqual([], self.resource_provider_show_usage(rp['uuid']))
|
||||
|
||||
|
||||
class TestResourceUsage(base.BaseTestCase):
|
||||
VERSION = '1.9'
|
||||
|
||||
def test_usage_by_project_id_user_id(self):
|
||||
c1 = str(uuid.uuid4())
|
||||
c2 = str(uuid.uuid4())
|
||||
c3 = str(uuid.uuid4())
|
||||
p1 = str(uuid.uuid4())
|
||||
p2 = str(uuid.uuid4())
|
||||
u1 = str(uuid.uuid4())
|
||||
u2 = str(uuid.uuid4())
|
||||
|
||||
rp = self.resource_provider_create()
|
||||
self.resource_inventory_set(rp['uuid'], 'VCPU=16')
|
||||
self.resource_allocation_set(
|
||||
c1, ['rp={},VCPU=2'.format(rp['uuid'])], project_id=p1, user_id=u1)
|
||||
self.resource_allocation_set(
|
||||
c2, ['rp={},VCPU=4'.format(rp['uuid'])], project_id=p2, user_id=u1)
|
||||
self.resource_allocation_set(
|
||||
c3, ['rp={},VCPU=6'.format(rp['uuid'])], project_id=p1, user_id=u2)
|
||||
|
||||
# Show usage on the resource provider for all consumers.
|
||||
self.assertEqual(
|
||||
12, self.resource_provider_show_usage(uuid=rp['uuid'])[0]['usage'])
|
||||
# Show usage for project p1.
|
||||
self.assertEqual(
|
||||
8, self.resource_show_usage(project_id=p1)[0]['usage'])
|
||||
# Show usage for project p1 and user u1.
|
||||
self.assertEqual(
|
||||
2, self.resource_show_usage(
|
||||
project_id=p1, user_id=u1)[0]['usage'])
|
||||
# Show usage for project p2.
|
||||
self.assertEqual(
|
||||
4, self.resource_show_usage(project_id=p2)[0]['usage'])
|
||||
|
|
|
@ -23,6 +23,8 @@ SUPPORTED_VERSIONS = [
|
|||
'1.5',
|
||||
'1.6',
|
||||
'1.7',
|
||||
'1.8',
|
||||
'1.9',
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
The ``openstack resource provider allocation set`` command now supports
|
||||
microversion `1.8`_. Specifically from 1.8 it is necessary to specify
|
||||
``--user-id`` and ``--project-id`` arguments when setting allocations.
|
||||
|
||||
The ``openstack resource usage show`` command is
|
||||
available starting from microversion `1.9`_. It is possible to
|
||||
show usages for a project and user.
|
||||
|
||||
See the command documentation for `allocation set`_ and
|
||||
`resource usage show`_ for more details.
|
||||
|
||||
.. _1.8: https://docs.openstack.org/nova/latest/user/placement.html#require-placement-project-id-user-id-in-put-allocations
|
||||
.. _1.9: https://docs.openstack.org/nova/latest/user/placement.html#add-get-usages
|
||||
.. _allocation set: https://docs.openstack.org/osc-placement/latest/cli/index.html#resource-provider-allocation-set
|
||||
.. _resource usage show: https://docs.openstack.org/osc-placement/latest/cli/index.html#resource-usage-show
|
|
@ -35,7 +35,8 @@ openstack.placement.v1 =
|
|||
resource_provider_show = osc_placement.resources.resource_provider:ShowResourceProvider
|
||||
resource_provider_set = osc_placement.resources.resource_provider:SetResourceProvider
|
||||
resource_provider_delete = osc_placement.resources.resource_provider:DeleteResourceProvider
|
||||
resource provider_usage_show = osc_placement.resources.usage:ShowUsage
|
||||
resource_provider_usage_show = osc_placement.resources.usage:ShowUsage
|
||||
resource_usage_show = osc_placement.resources.usage:ResourceShowUsage
|
||||
resource_provider_inventory_set = osc_placement.resources.inventory:SetInventory
|
||||
resource_provider_inventory_class_set = osc_placement.resources.inventory:SetClassInventory
|
||||
resource_provider_inventory_list = osc_placement.resources.inventory:ListInventory
|
||||
|
|
Loading…
Reference in New Issue