Add group types and group specs
This patch adds support for group types and group specs in the client. Server patch is merged: https://review.openstack.org/#/c/320165/ Current microversion is 3.11. The following CLI's are supported. cinder --os-volume-api-version 3.11 group-type-create my_test_group cinder --os-volume-api-version 3.11 group-type-list cinder --os-volume-api-version 3.11 group-type-show my_test_group cinder --os-volume-api-version 3.11 group-type-key my_test_group set test_key=test_val cinder --os-volume-api-version 3.11 group-specs-list cinder --os-volume-api-version 3.11 group-type-key my_test_group unset test_key cinder --os-volume-api-version 3.11 group-type-update <group type uuid> --name "new_group" --description "my group type" cinder --os-volume-api-version 3.11 group-type-delete new_group Change-Id: I161a96aa53208e78146cb115d500fd6b2c42d046 Partial-Implements: blueprint generic-volume-group
This commit is contained in:
@@ -32,7 +32,7 @@ if not LOG.handlers:
|
||||
|
||||
# key is a deprecated version and value is an alternative version.
|
||||
DEPRECATED_VERSIONS = {"1": "2"}
|
||||
MAX_VERSION = "3.7"
|
||||
MAX_VERSION = "3.11"
|
||||
|
||||
_SUBSTITUTIONS = {}
|
||||
|
||||
|
||||
@@ -184,3 +184,67 @@ class FakeHTTPClient(fake_v2.FakeHTTPClient):
|
||||
tenant_id='0fa851f6668144cf9cd8c8419c1646c1')
|
||||
return (200, {},
|
||||
{'backups': backup})
|
||||
|
||||
#
|
||||
# GroupTypes
|
||||
#
|
||||
def get_group_types(self, **kw):
|
||||
return (200, {}, {
|
||||
'group_types': [{'id': 1,
|
||||
'name': 'test-type-1',
|
||||
'description': 'test_type-1-desc',
|
||||
'group_specs': {}},
|
||||
{'id': 2,
|
||||
'name': 'test-type-2',
|
||||
'description': 'test_type-2-desc',
|
||||
'group_specs': {}}]})
|
||||
|
||||
def get_group_types_1(self, **kw):
|
||||
return (200, {}, {'group_type': {'id': 1,
|
||||
'name': 'test-type-1',
|
||||
'description': 'test_type-1-desc',
|
||||
'group_specs': {u'key': u'value'}}})
|
||||
|
||||
def get_group_types_2(self, **kw):
|
||||
return (200, {}, {'group_type': {'id': 2,
|
||||
'name': 'test-type-2',
|
||||
'description': 'test_type-2-desc',
|
||||
'group_specs': {}}})
|
||||
|
||||
def get_group_types_3(self, **kw):
|
||||
return (200, {}, {'group_type': {'id': 3,
|
||||
'name': 'test-type-3',
|
||||
'description': 'test_type-3-desc',
|
||||
'group_specs': {},
|
||||
'is_public': False}})
|
||||
|
||||
def get_group_types_default(self, **kw):
|
||||
return self.get_group_types_1()
|
||||
|
||||
def post_group_types(self, body, **kw):
|
||||
return (202, {}, {'group_type': {'id': 3,
|
||||
'name': 'test-type-3',
|
||||
'description': 'test_type-3-desc',
|
||||
'group_specs': {}}})
|
||||
|
||||
def post_group_types_1_group_specs(self, body, **kw):
|
||||
assert list(body) == ['group_specs']
|
||||
return (200, {}, {'group_specs': {'k': 'v'}})
|
||||
|
||||
def delete_group_types_1_group_specs_k(self, **kw):
|
||||
return(204, {}, None)
|
||||
|
||||
def delete_group_types_1_group_specs_m(self, **kw):
|
||||
return(204, {}, None)
|
||||
|
||||
def delete_group_types_1(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
def delete_group_types_3_group_specs_k(self, **kw):
|
||||
return(204, {}, None)
|
||||
|
||||
def delete_group_types_3(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
def put_group_types_1(self, **kw):
|
||||
return self.get_group_types_1()
|
||||
|
||||
99
cinderclient/tests/unit/v3/test_group_types.py
Normal file
99
cinderclient/tests/unit/v3/test_group_types.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# Copyright (c) 2016 EMC Corporation
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 cinderclient.v3 import group_types
|
||||
from cinderclient.tests.unit import utils
|
||||
from cinderclient.tests.unit.v3 import fakes
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class GroupTypesTest(utils.TestCase):
|
||||
|
||||
def test_list_group_types(self):
|
||||
tl = cs.group_types.list()
|
||||
cs.assert_called('GET', '/group_types?is_public=None')
|
||||
self._assert_request_id(tl)
|
||||
for t in tl:
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
|
||||
def test_list_group_types_not_public(self):
|
||||
t1 = cs.group_types.list(is_public=None)
|
||||
cs.assert_called('GET', '/group_types?is_public=None')
|
||||
self._assert_request_id(t1)
|
||||
|
||||
def test_create(self):
|
||||
t = cs.group_types.create('test-type-3', 'test-type-3-desc')
|
||||
cs.assert_called('POST', '/group_types',
|
||||
{'group_type': {
|
||||
'name': 'test-type-3',
|
||||
'description': 'test-type-3-desc',
|
||||
'is_public': True
|
||||
}})
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
self._assert_request_id(t)
|
||||
|
||||
def test_create_non_public(self):
|
||||
t = cs.group_types.create('test-type-3', 'test-type-3-desc', False)
|
||||
cs.assert_called('POST', '/group_types',
|
||||
{'group_type': {
|
||||
'name': 'test-type-3',
|
||||
'description': 'test-type-3-desc',
|
||||
'is_public': False
|
||||
}})
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
self._assert_request_id(t)
|
||||
|
||||
def test_update(self):
|
||||
t = cs.group_types.update('1', 'test_type_1', 'test_desc_1', False)
|
||||
cs.assert_called('PUT',
|
||||
'/group_types/1',
|
||||
{'group_type': {'name': 'test_type_1',
|
||||
'description': 'test_desc_1',
|
||||
'is_public': False}})
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
self._assert_request_id(t)
|
||||
|
||||
def test_get(self):
|
||||
t = cs.group_types.get('1')
|
||||
cs.assert_called('GET', '/group_types/1')
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
self._assert_request_id(t)
|
||||
|
||||
def test_default(self):
|
||||
t = cs.group_types.default()
|
||||
cs.assert_called('GET', '/group_types/default')
|
||||
self.assertIsInstance(t, group_types.GroupType)
|
||||
self._assert_request_id(t)
|
||||
|
||||
def test_set_key(self):
|
||||
t = cs.group_types.get(1)
|
||||
res = t.set_keys({'k': 'v'})
|
||||
cs.assert_called('POST',
|
||||
'/group_types/1/group_specs',
|
||||
{'group_specs': {'k': 'v'}})
|
||||
self._assert_request_id(res)
|
||||
|
||||
def test_unset_keys(self):
|
||||
t = cs.group_types.get(1)
|
||||
res = t.unset_keys(['k'])
|
||||
cs.assert_called('DELETE', '/group_types/1/group_specs/k')
|
||||
self._assert_request_id(res)
|
||||
|
||||
def test_delete(self):
|
||||
t = cs.group_types.delete(1)
|
||||
cs.assert_called('DELETE', '/group_types/1')
|
||||
self._assert_request_id(t)
|
||||
@@ -133,3 +133,41 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command,
|
||||
'--os-volume-api-version 3.8 '
|
||||
'backup-update --name new-name 1234')
|
||||
|
||||
def test_group_type_list(self):
|
||||
self.run_command('--os-volume-api-version 3.11 group-type-list')
|
||||
self.assert_called_anytime('GET', '/group_types?is_public=None')
|
||||
|
||||
def test_group_type_show(self):
|
||||
self.run_command('--os-volume-api-version 3.11 '
|
||||
'group-type-show 1')
|
||||
self.assert_called('GET', '/group_types/1')
|
||||
|
||||
def test_group_type_create(self):
|
||||
self.run_command('--os-volume-api-version 3.11 '
|
||||
'group-type-create test-type-1')
|
||||
self.assert_called('POST', '/group_types')
|
||||
|
||||
def test_group_type_create_public(self):
|
||||
expected = {'group_type': {'name': 'test-type-1',
|
||||
'description': 'test_type-1-desc',
|
||||
'is_public': True}}
|
||||
self.run_command('--os-volume-api-version 3.11 '
|
||||
'group-type-create test-type-1 '
|
||||
'--description=test_type-1-desc '
|
||||
'--is-public=True')
|
||||
self.assert_called('POST', '/group_types', body=expected)
|
||||
|
||||
def test_group_type_create_private(self):
|
||||
expected = {'group_type': {'name': 'test-type-3',
|
||||
'description': 'test_type-3-desc',
|
||||
'is_public': False}}
|
||||
self.run_command('--os-volume-api-version 3.11 '
|
||||
'group-type-create test-type-3 '
|
||||
'--description=test_type-3-desc '
|
||||
'--is-public=False')
|
||||
self.assert_called('POST', '/group_types', body=expected)
|
||||
|
||||
def test_group_specs_list(self):
|
||||
self.run_command('--os-volume-api-version 3.11 group-specs-list')
|
||||
self.assert_called('GET', '/group_types?is_public=None')
|
||||
|
||||
@@ -22,6 +22,7 @@ from cinderclient.v3 import cgsnapshots
|
||||
from cinderclient.v3 import clusters
|
||||
from cinderclient.v3 import consistencygroups
|
||||
from cinderclient.v3 import capabilities
|
||||
from cinderclient.v3 import group_types
|
||||
from cinderclient.v3 import limits
|
||||
from cinderclient.v3 import pools
|
||||
from cinderclient.v3 import qos_specs
|
||||
@@ -70,6 +71,7 @@ class Client(object):
|
||||
self.volumes = volumes.VolumeManager(self)
|
||||
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
||||
self.volume_types = volume_types.VolumeTypeManager(self)
|
||||
self.group_types = group_types.GroupTypeManager(self)
|
||||
self.volume_type_access = \
|
||||
volume_type_access.VolumeTypeAccessManager(self)
|
||||
self.volume_encryption_types = \
|
||||
|
||||
148
cinderclient/v3/group_types.py
Normal file
148
cinderclient/v3/group_types.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# Copyright (c) 2016 EMC Corporation
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
"""Group Type interface."""
|
||||
|
||||
from cinderclient import base
|
||||
|
||||
|
||||
class GroupType(base.Resource):
|
||||
"""A Group Type is the type of group to be created."""
|
||||
def __repr__(self):
|
||||
return "<GroupType: %s>" % self.name
|
||||
|
||||
@property
|
||||
def is_public(self):
|
||||
"""
|
||||
Provide a user-friendly accessor to is_public
|
||||
"""
|
||||
return self._info.get("is_public",
|
||||
self._info.get("is_public", 'N/A'))
|
||||
|
||||
def get_keys(self):
|
||||
"""Get group specs from a group type.
|
||||
|
||||
:param type: The :class:`GroupType` to get specs from
|
||||
"""
|
||||
_resp, body = self.manager.api.client.get(
|
||||
"/group_types/%s/group_specs" %
|
||||
base.getid(self))
|
||||
return body["group_specs"]
|
||||
|
||||
def set_keys(self, metadata):
|
||||
"""Set group specs on a group type.
|
||||
|
||||
:param type : The :class:`GroupType` to set spec on
|
||||
:param metadata: A dict of key/value pairs to be set
|
||||
"""
|
||||
body = {'group_specs': metadata}
|
||||
return self.manager._create(
|
||||
"/group_types/%s/group_specs" % base.getid(self),
|
||||
body,
|
||||
"group_specs",
|
||||
return_raw=True)
|
||||
|
||||
def unset_keys(self, keys):
|
||||
"""Unset specs on a group type.
|
||||
|
||||
:param type_id: The :class:`GroupType` to unset spec on
|
||||
:param keys: A list of keys to be unset
|
||||
"""
|
||||
|
||||
for k in keys:
|
||||
resp = self.manager._delete(
|
||||
"/group_types/%s/group_specs/%s" % (
|
||||
base.getid(self), k))
|
||||
if resp:
|
||||
return resp
|
||||
|
||||
|
||||
class GroupTypeManager(base.ManagerWithFind):
|
||||
"""Manage :class:`GroupType` resources."""
|
||||
resource_class = GroupType
|
||||
|
||||
def list(self, search_opts=None, is_public=None):
|
||||
"""Lists all group types.
|
||||
|
||||
:rtype: list of :class:`GroupType`.
|
||||
"""
|
||||
query_string = ''
|
||||
if not is_public:
|
||||
query_string = '?is_public=%s' % is_public
|
||||
return self._list("/group_types%s" % (query_string), "group_types")
|
||||
|
||||
def get(self, group_type):
|
||||
"""Get a specific group type.
|
||||
|
||||
:param group_type: The ID of the :class:`GroupType` to get.
|
||||
:rtype: :class:`GroupType`
|
||||
"""
|
||||
return self._get("/group_types/%s" % base.getid(group_type),
|
||||
"group_type")
|
||||
|
||||
def default(self):
|
||||
"""Get the default group type.
|
||||
|
||||
:rtype: :class:`GroupType`
|
||||
"""
|
||||
return self._get("/group_types/default", "group_type")
|
||||
|
||||
def delete(self, group_type):
|
||||
"""Deletes a specific group_type.
|
||||
|
||||
:param group_type: The name or ID of the :class:`GroupType` to get.
|
||||
"""
|
||||
return self._delete("/group_types/%s" % base.getid(group_type))
|
||||
|
||||
def create(self, name, description=None, is_public=True):
|
||||
"""Creates a group type.
|
||||
|
||||
:param name: Descriptive name of the group type
|
||||
:param description: Description of the the group type
|
||||
:param is_public: Group type visibility
|
||||
:rtype: :class:`GroupType`
|
||||
"""
|
||||
|
||||
body = {
|
||||
"group_type": {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"is_public": is_public,
|
||||
}
|
||||
}
|
||||
|
||||
return self._create("/group_types", body, "group_type")
|
||||
|
||||
def update(self, group_type, name=None, description=None, is_public=None):
|
||||
"""Update the name and/or description for a group type.
|
||||
|
||||
:param group_type: The ID of the :class:`GroupType` to update.
|
||||
:param name: Descriptive name of the group type.
|
||||
:param description: Description of the the group type.
|
||||
:rtype: :class:`GroupType`
|
||||
"""
|
||||
|
||||
body = {
|
||||
"group_type": {
|
||||
"name": name,
|
||||
"description": description
|
||||
}
|
||||
}
|
||||
if is_public is not None:
|
||||
body["group_type"]["is_public"] = is_public
|
||||
|
||||
return self._update("/group_types/%s" % base.getid(group_type),
|
||||
body, response_key="group_type")
|
||||
@@ -73,6 +73,11 @@ def _find_vtype(cs, vtype):
|
||||
return utils.find_resource(cs.volume_types, vtype)
|
||||
|
||||
|
||||
def _find_gtype(cs, gtype):
|
||||
"""Gets a group type by name or ID."""
|
||||
return utils.find_resource(cs.group_types, gtype)
|
||||
|
||||
|
||||
def _find_backup(cs, backup):
|
||||
"""Gets a backup by name or ID."""
|
||||
return utils.find_resource(cs.backups, backup)
|
||||
@@ -873,6 +878,10 @@ def _print_volume_type_list(vtypes):
|
||||
utils.print_list(vtypes, ['ID', 'Name', 'Description', 'Is_Public'])
|
||||
|
||||
|
||||
def _print_group_type_list(gtypes):
|
||||
utils.print_list(gtypes, ['ID', 'Name', 'Description'])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
def do_type_list(cs, args):
|
||||
"""Lists available 'volume types'. (Admin only will see private types)"""
|
||||
@@ -880,6 +889,14 @@ def do_type_list(cs, args):
|
||||
_print_volume_type_list(vtypes)
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
def do_group_type_list(cs, args):
|
||||
"""Lists available 'group types'. (Admin only will see private types)"""
|
||||
gtypes = cs.group_types.list()
|
||||
_print_group_type_list(gtypes)
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
def do_type_default(cs, args):
|
||||
"""List the default volume type."""
|
||||
@@ -887,6 +904,14 @@ def do_type_default(cs, args):
|
||||
_print_volume_type_list([vtype])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
def do_group_type_default(cs, args):
|
||||
"""List the default group type."""
|
||||
gtype = cs.group_types.default()
|
||||
_print_group_type_list([gtype])
|
||||
|
||||
|
||||
@utils.arg('volume_type',
|
||||
metavar='<volume_type>',
|
||||
help='Name or ID of the volume type.')
|
||||
@@ -901,6 +926,21 @@ def do_type_show(cs, args):
|
||||
utils.print_dict(info, formatters=['extra_specs'])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
@utils.arg('group_type',
|
||||
metavar='<group_type>',
|
||||
help='Name or ID of the group type.')
|
||||
def do_group_type_show(cs, args):
|
||||
"""Show group type details."""
|
||||
gtype = _find_gtype(cs, args.group_type)
|
||||
info = dict()
|
||||
info.update(gtype._info)
|
||||
|
||||
info.pop('links', None)
|
||||
utils.print_dict(info, formatters=['group_specs'])
|
||||
|
||||
|
||||
@utils.arg('id',
|
||||
metavar='<id>',
|
||||
help='ID of the volume type.')
|
||||
@@ -922,6 +962,28 @@ def do_type_update(cs, args):
|
||||
_print_volume_type_list([vtype])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
@utils.arg('id',
|
||||
metavar='<id>',
|
||||
help='ID of the group type.')
|
||||
@utils.arg('--name',
|
||||
metavar='<name>',
|
||||
help='Name of the group type.')
|
||||
@utils.arg('--description',
|
||||
metavar='<description>',
|
||||
help='Description of the group type.')
|
||||
@utils.arg('--is-public',
|
||||
metavar='<is-public>',
|
||||
help='Make type accessible to the public or not.')
|
||||
def do_group_type_update(cs, args):
|
||||
"""Updates group type name, description, and/or is_public."""
|
||||
is_public = strutils.bool_from_string(args.is_public)
|
||||
gtype = cs.group_types.update(args.id, args.name, args.description,
|
||||
is_public)
|
||||
_print_group_type_list([gtype])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
def do_extra_specs_list(cs, args):
|
||||
"""Lists current volume types and extra specs."""
|
||||
@@ -929,6 +991,14 @@ def do_extra_specs_list(cs, args):
|
||||
utils.print_list(vtypes, ['ID', 'Name', 'extra_specs'])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
def do_group_specs_list(cs, args):
|
||||
"""Lists current group types and specs."""
|
||||
gtypes = cs.group_types.list()
|
||||
utils.print_list(gtypes, ['ID', 'Name', 'group_specs'])
|
||||
|
||||
|
||||
@utils.arg('name',
|
||||
metavar='<name>',
|
||||
help='Name of new volume type.')
|
||||
@@ -947,6 +1017,25 @@ def do_type_create(cs, args):
|
||||
_print_volume_type_list([vtype])
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
@utils.arg('name',
|
||||
metavar='<name>',
|
||||
help='Name of new group type.')
|
||||
@utils.arg('--description',
|
||||
metavar='<description>',
|
||||
help='Description of new group type.')
|
||||
@utils.arg('--is-public',
|
||||
metavar='<is-public>',
|
||||
default=True,
|
||||
help='Make type accessible to the public (default true).')
|
||||
def do_group_type_create(cs, args):
|
||||
"""Creates a group type."""
|
||||
is_public = strutils.bool_from_string(args.is_public)
|
||||
gtype = cs.group_types.create(args.name, args.description, is_public)
|
||||
_print_group_type_list([gtype])
|
||||
|
||||
|
||||
@utils.arg('vol_type',
|
||||
metavar='<vol_type>', nargs='+',
|
||||
help='Name or ID of volume type or types to delete.')
|
||||
@@ -968,6 +1057,28 @@ def do_type_delete(cs, args):
|
||||
"specified types.")
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
@utils.arg('group_type',
|
||||
metavar='<group_type>', nargs='+',
|
||||
help='Name or ID of group type or types to delete.')
|
||||
def do_group_type_delete(cs, args):
|
||||
"""Deletes group type or types."""
|
||||
failure_count = 0
|
||||
for group_type in args.group_type:
|
||||
try:
|
||||
gtype = _find_group_type(cs, group_type)
|
||||
cs.group_types.delete(gtype)
|
||||
print("Request to delete group type %s has been accepted."
|
||||
% (group_type))
|
||||
except Exception as e:
|
||||
failure_count += 1
|
||||
print("Delete for group type %s failed: %s" % (group_type, e))
|
||||
if failure_count == len(args.group_type):
|
||||
raise exceptions.CommandError("Unable to delete any of the "
|
||||
"specified types.")
|
||||
|
||||
|
||||
@utils.arg('vtype',
|
||||
metavar='<vtype>',
|
||||
help='Name or ID of volume type.')
|
||||
@@ -993,6 +1104,32 @@ def do_type_key(cs, args):
|
||||
vtype.unset_keys(list(keypair))
|
||||
|
||||
|
||||
@utils.service_type('volumev3')
|
||||
@api_versions.wraps('3.11')
|
||||
@utils.arg('gtype',
|
||||
metavar='<gtype>',
|
||||
help='Name or ID of group type.')
|
||||
@utils.arg('action',
|
||||
metavar='<action>',
|
||||
choices=['set', 'unset'],
|
||||
help='The action. Valid values are "set" or "unset."')
|
||||
@utils.arg('metadata',
|
||||
metavar='<key=value>',
|
||||
nargs='+',
|
||||
default=[],
|
||||
help='The group specs key and value pair to set or unset. '
|
||||
'For unset, specify only the key.')
|
||||
def do_group_type_key(cs, args):
|
||||
"""Sets or unsets group_spec for a group type."""
|
||||
gtype = _find_group_type(cs, args.gtype)
|
||||
keypair = _extract_metadata(args)
|
||||
|
||||
if args.action == 'set':
|
||||
gtype.set_keys(keypair)
|
||||
elif args.action == 'unset':
|
||||
gtype.unset_keys(list(keypair))
|
||||
|
||||
|
||||
@utils.arg('--volume-type', metavar='<volume_type>', required=True,
|
||||
help='Filter results by volume type name or ID.')
|
||||
@utils.service_type('volumev3')
|
||||
@@ -1250,6 +1387,11 @@ def _find_volume_type(cs, vtype):
|
||||
return utils.find_resource(cs.volume_types, vtype)
|
||||
|
||||
|
||||
def _find_group_type(cs, gtype):
|
||||
"""Gets a group type by name or ID."""
|
||||
return utils.find_resource(cs.group_types, gtype)
|
||||
|
||||
|
||||
@utils.arg('volume',
|
||||
metavar='<volume>',
|
||||
help='Name or ID of volume to snapshot.')
|
||||
|
||||
Reference in New Issue
Block a user