From 0a5493f264902f8f21b87f3fcc792997ac7bfb85 Mon Sep 17 00:00:00 2001 From: Andrey Volkov Date: Mon, 23 Oct 2017 10:37:39 +0300 Subject: [PATCH] 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 --- osc_placement/plugin.py | 17 +++---- osc_placement/resources/inventory.py | 34 ++++++++++---- osc_placement/tests/functional/base.py | 8 ++-- .../tests/functional/test_inventory.py | 18 ++++++++ osc_placement/version.py | 45 ++++++++++++++++--- .../microversion-1.5-0c6342c887669b8e.yaml | 18 ++++++++ 6 files changed, 111 insertions(+), 29 deletions(-) create mode 100644 releasenotes/notes/microversion-1.5-0c6342c887669b8e.yaml diff --git a/osc_placement/plugin.py b/osc_placement/plugin.py index 9fa227d..372833a 100644 --- a/osc_placement/plugin.py +++ b/osc_placement/plugin.py @@ -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='', 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 diff --git a/osc_placement/resources/inventory.py b/osc_placement/resources/inventory.py index b2a284d..f0ae946 100644 --- a/osc_placement/resources/inventory.py +++ b/osc_placement/resources/inventory.py @@ -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='', - 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): diff --git a/osc_placement/tests/functional/base.py b/osc_placement/tests/functional/base.py index b0973b6..653e3b5 100644 --- a/osc_placement/tests/functional/base.py +++ b/osc_placement/tests/functional/base.py @@ -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): diff --git a/osc_placement/tests/functional/test_inventory.py b/osc_placement/tests/functional/test_inventory.py index 332f257..d0756b7 100644 --- a/osc_placement/tests/functional/test_inventory.py +++ b/osc_placement/tests/functional/test_inventory.py @@ -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'])) diff --git a/osc_placement/version.py b/osc_placement/version.py index 470c92e..14d161c 100644 --- a/osc_placement/version.py +++ b/osc_placement/version.py @@ -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) diff --git a/releasenotes/notes/microversion-1.5-0c6342c887669b8e.yaml b/releasenotes/notes/microversion-1.5-0c6342c887669b8e.yaml new file mode 100644 index 0000000..85e4fbc --- /dev/null +++ b/releasenotes/notes/microversion-1.5-0c6342c887669b8e.yaml @@ -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.