RP delete inventories (v1.5)

UpgradeImpact: this change will require a major version
release since a previously required argument (resource_class)
is now an option (--resource_class) for the
"openstack resource provider inventory delete" CLI.

Change-Id: I4be95feeadcbf82ab23fa2709b72db4d4203c1e7
Partially-Implements: blueprint placement-osc-plugin-rocky
This commit is contained in:
Andrey Volkov 2017-10-23 10:37:39 +03:00 committed by Matt Riedemann
parent 844414861a
commit 0a5493f264
6 changed files with 111 additions and 29 deletions

View File

@ -15,21 +15,15 @@
import logging
from osc_lib import utils
from osc_placement import version
LOG = logging.getLogger(__name__)
API_NAME = 'placement'
API_VERSION_OPTION = 'os_placement_api_version'
SUPPORTED_VERSIONS = [
'1.0',
'1.1',
'1.2',
'1.3',
'1.4'
]
API_VERSIONS = {v: 'osc_placement.http.SessionClient'
for v in SUPPORTED_VERSIONS}
for v in version.SUPPORTED_VERSIONS}
def make_client(instance):
@ -51,13 +45,14 @@ def make_client(instance):
def build_option_parser(parser):
default = version.SUPPORTED_VERSIONS[0]
parser.add_argument(
'--os-placement-api-version',
metavar='<placement-api-version>',
default=utils.env(
'OS_PLACEMENT_API_VERSION',
default='1.0'
default=default
),
help='Placement API version, default=1.0'
)
help='Placement API version, default=%s' % default)
return parser

View File

@ -15,6 +15,8 @@ from collections import defaultdict
from osc_lib.command import command
from osc_lib import utils
from osc_placement import version
BASE_URL = '/resource_providers/{uuid}/inventories'
PER_CLASS_URL = BASE_URL + '/{resource_class}'
@ -211,10 +213,17 @@ class SetClassInventory(command.ShowOne):
return FIELDS, utils.get_dict_properties(resource, FIELDS)
# TODO(avolkov): Add delete all inventories for RP (version 1.5)
class DeleteInventory(command.Command):
class DeleteInventory(command.Command, version.CheckerMixin):
"""Delete the inventory for a given resource provider/class pair"""
"""Delete the inventory.
Depending on the resource class argument presence, it can
delete all inventory for a given resource provider or for a resource
provider/class pair.
Delete all inventories for given resource provider
requires at least ``--os-placement-api-version 1.5``.
"""
def get_parser(self, prog_name):
parser = super(DeleteInventory, self).get_parser(prog_name)
@ -225,19 +234,28 @@ class DeleteInventory(command.Command):
help='UUID of the resource provider'
)
parser.add_argument(
'resource_class',
'--resource-class',
metavar='<resource_class>',
help=RC_HELP
required=self.compare_version(version.lt('1.5')),
help=(RC_HELP +
'\nThis argument can be omitted starting with '
'``--os-placement-api-version 1.5``. If it is omitted all '
'inventories of the specified resource provider '
'will be deleted.')
)
return parser
def take_action(self, parsed_args):
http = self.app.client_manager.placement
url = BASE_URL
params = {'uuid': parsed_args.uuid}
if parsed_args.resource_class is not None:
url = PER_CLASS_URL
params = {'uuid': parsed_args.uuid,
'resource_class': parsed_args.resource_class}
url = PER_CLASS_URL.format(uuid=parsed_args.uuid,
resource_class=parsed_args.resource_class)
http.request('DELETE', url)
http.request('DELETE', url.format(**params))
class ShowInventory(command.ShowOne):

View File

@ -147,10 +147,10 @@ class BaseTestCase(base.BaseTestCase):
return self.openstack('resource provider inventory list ' + uuid,
use_json=True)
def resource_inventory_delete(self, uuid, resource_class):
cmd = 'resource provider inventory delete {uuid} {rc}'.format(
uuid=uuid, rc=resource_class
)
def resource_inventory_delete(self, uuid, resource_class=None):
cmd = 'resource provider inventory delete {uuid}'.format(uuid=uuid)
if resource_class:
cmd += ' --resource-class ' + resource_class
self.openstack(cmd)
def resource_inventory_set(self, uuid, *resources):

View File

@ -63,6 +63,14 @@ class TestInventory(base.BaseTestCase):
self.assertIn('No inventory of class VCPU found for delete',
exc.output.decode('utf-8'))
def test_delete_all_inventories(self):
# Negative test to assert command failure because
# microversion < 1.5 and --resource-class is not specified.
self.assertCommandFailed(
'argument --resource-class is required',
self.resource_inventory_delete,
'fake_uuid')
class TestSetInventory(base.BaseTestCase):
def test_fail_if_no_rp(self):
@ -194,3 +202,13 @@ class TestSetInventory(base.BaseTestCase):
self.assertEqual(128, inv['MEMORY_MB']['total'])
self.assertEqual(16, inv['MEMORY_MB']['step_size'])
self.assertEqual(32, inv['VCPU']['total'])
class TestInventory15(TestInventory):
VERSION = '1.5'
def test_delete_all_inventories(self):
rp = self.resource_provider_create()
self.resource_inventory_set(rp['uuid'], 'MEMORY_MB=16', 'VCPU=32')
self.resource_inventory_delete(rp['uuid'])
self.assertEqual([], self.resource_inventory_list(rp['uuid']))

View File

@ -14,6 +14,16 @@ from distutils.version import StrictVersion
import operator
SUPPORTED_VERSIONS = [
'1.0',
'1.1',
'1.2',
'1.3',
'1.4',
'1.5',
]
def _op(func, b):
return lambda a: func(StrictVersion(a), StrictVersion(b))
@ -48,6 +58,16 @@ def _compare(ver, *predicates, **kwargs):
def compare(ver, *predicates, **kwargs):
"""Validate version satisfies provided predicates.
kwargs['exc'] - boolean whether exception should be raised
kwargs['op'] - (all, any) how predicates should be checked
Examples:
compare('1.1', version.gt('1.2'), exc=False) - False
compare('1.1', version.eq('1.0'), version.eq('1.1'), op=any) - True
"""
exc = kwargs.get('exc', True)
if not _compare(ver, *predicates, **kwargs):
if exc:
@ -58,20 +78,33 @@ def compare(ver, *predicates, **kwargs):
def check(*predicates, **check_kwargs):
"""Decorator for command object method.
See `compare`
"""
def wrapped(func):
def inner(self, *args, **kwargs):
version = self.app.client_manager.placement.api_version
compare(version, *predicates, **check_kwargs)
compare(get_version(self), *predicates, **check_kwargs)
return func(self, *args, **kwargs)
return inner
return wrapped
def get_version(obj):
"""Extract version from a command object."""
try:
version = obj.app.client_manager.placement.api_version
except AttributeError:
# resource does not have api_version attr when docs are generated
# so let's use the minimal one
version = SUPPORTED_VERSIONS[0]
return version
class CheckerMixin(object):
def check_version(self, *predicates, **kwargs):
version = self.app.client_manager.placement.api_version
return compare(version, *predicates, **kwargs)
return compare(get_version(self), *predicates, **kwargs)
def compare_version(self, *predicates, **kwargs):
version = self.app.client_manager.placement.api_version
return compare(version, *predicates, exc=False, **kwargs)
return compare(get_version(self), *predicates, exc=False, **kwargs)

View File

@ -0,0 +1,18 @@
---
features:
- |
The ``openstack resource provider inventory delete`` command now supports
microversion `1.5`_. Specifically it is possible to delete all inventories
of the specified resource provider.
See the `command documentation`__ for more details.
.. _1.5: https://docs.openstack.org/nova/latest/user/placement.html#delete-all-inventory-for-a-resource-provider
.. __: https://docs.openstack.org/osc-placement/latest/cli/index.html#resource-provider-inventory-delete
upgrade:
- |
The ``resource_class`` positional argument in command
``openstack resource provider inventory delete`` was replaced with the
``--resource-class`` optional argument. The ``--resource-class`` option
is still required if using ``--os-placement-api-version`` less than
1.5.