Add commands for default type overrides

This patch adds command for set,get and delete default volume types
for projects.

This patch adds 3 commands :

1) Set
Set a default volume type for a project
cinder --os-volume-api-version 3.62 default-type-set <vol-type-id> <project-id>

2) Get
Get the default volume type for a project
cinder --os-volume-api-version 3.62 default-type-list --project-id <project-id>
Get all default types
cinder --os-volume-api-version 3.62 default-type-list

3) Unset
Unset default volume type for a project
cinder --os-volume-api-version 3.62 default-type-unset <project-id>

Implements: Blueprint multiple-default-volume-types

Change-Id: Id2fb00c218edbb98df3193577dba6a897c6e73f6
This commit is contained in:
whoami-rajat 2020-07-03 12:31:48 +00:00 committed by Brian Rosmaita
parent 76f2b91d9a
commit 7ee7d376a1
10 changed files with 277 additions and 1 deletions

View File

@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
# key is a deprecated version and value is an alternative version. # key is a deprecated version and value is an alternative version.
DEPRECATED_VERSIONS = {"2": "3"} DEPRECATED_VERSIONS = {"2": "3"}
DEPRECATED_VERSION = "2.0" DEPRECATED_VERSION = "2.0"
MAX_VERSION = "3.61" MAX_VERSION = "3.62"
MIN_VERSION = "3.0" MIN_VERSION = "3.0"
_SUBSTITUTIONS = {} _SUBSTITUTIONS = {}

View File

@ -331,6 +331,26 @@ class Manager(common_base.HookableMixin):
else: else:
return self.resource_class(self, body, loaded=True) return self.resource_class(self, body, loaded=True)
def _get_all_with_base_url(self, url, response_key=None):
resp, body = self.api.client.get_with_base_url(url)
if response_key:
if isinstance(body[response_key], list):
return [self.resource_class(self, res, loaded=True)
for res in body[response_key] if res]
return self.resource_class(self, body[response_key],
loaded=True)
return self.resource_class(self, body, loaded=True)
def _create_update_with_base_url(self, url, body, response_key=None):
resp, body = self.api.client.create_update_with_base_url(
url, body=body)
if response_key:
return self.resource_class(self, body[response_key], loaded=True)
return self.resource_class(self, body, loaded=True)
def _delete_with_base_url(self, url, response_key=None):
self.api.client.delete_with_base_url(url)
class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)): class ManagerWithFind(six.with_metaclass(abc.ABCMeta, Manager)):
""" """

View File

@ -269,6 +269,12 @@ class SessionClient(adapter.LegacyJsonAdapter):
def get_with_base_url(self, url, **kwargs): def get_with_base_url(self, url, **kwargs):
return self._cs_request_base_url(url, 'GET', **kwargs) return self._cs_request_base_url(url, 'GET', **kwargs)
def create_update_with_base_url(self, url, **kwargs):
return self._cs_request_base_url(url, 'PUT', **kwargs)
def delete_with_base_url(self, url, **kwargs):
return self._cs_request_base_url(url, 'DELETE', **kwargs)
class HTTPClient(object): class HTTPClient(object):

View File

@ -309,6 +309,30 @@ def _stub_server_versions():
] ]
def stub_default_type():
return {
'default_type': {
'project_id': '629632e7-99d2-4c40-9ae3-106fa3b1c9b7',
'volume_type_id': '4c298f16-e339-4c80-b934-6cbfcb7525a0'
}
}
def stub_default_types():
return {
'default_types': [
{
'project_id': '629632e7-99d2-4c40-9ae3-106fa3b1c9b7',
'volume_type_id': '4c298f16-e339-4c80-b934-6cbfcb7525a0'
},
{
'project_id': 'a0c01994-1245-416e-8fc9-1aca86329bfd',
'volume_type_id': 'ff094b46-f82a-4a74-9d9e-d3d08116ad93'
}
]
}
class FakeClient(fakes.FakeClient, client.Client): class FakeClient(fakes.FakeClient, client.Client):
def __init__(self, api_version=None, *args, **kwargs): def __init__(self, api_version=None, *args, **kwargs):
@ -1055,9 +1079,35 @@ class FakeHTTPClient(base_client.HTTPClient):
{'transfer': _stub_transfer(transfer1, base_uri, tenant_id)}) {'transfer': _stub_transfer(transfer1, base_uri, tenant_id)})
def get_with_base_url(self, url, **kw): def get_with_base_url(self, url, **kw):
if 'default-types' in url:
return self._cs_request(url, 'GET', **kw)
server_versions = _stub_server_versions() server_versions = _stub_server_versions()
return (200, {'versions': server_versions}) return (200, {'versions': server_versions})
def create_update_with_base_url(self, url, **kwargs):
return self._cs_request(url, 'PUT', **kwargs)
def put_v3_default_types_629632e7_99d2_4c40_9ae3_106fa3b1c9b7(
self, **kwargs):
default_type = stub_default_type()
return (200, {}, default_type)
def get_v3_default_types_629632e7_99d2_4c40_9ae3_106fa3b1c9b7(
self, **kw):
default_types = stub_default_type()
return (200, {}, default_types)
def get_v3_default_types(self, **kw):
default_types = stub_default_types()
return (200, {}, default_types)
def delete_with_base_url(self, url, **kwargs):
return self._cs_request(url, 'DELETE', **kwargs)
def delete_v3_default_types_629632e7_99d2_4c40_9ae3_106fa3b1c9b7(
self, **kwargs):
return (204, {}, {})
# #
# Services # Services
# #

View File

@ -0,0 +1,46 @@
# 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 import api_versions
from cinderclient.tests.unit import utils
from cinderclient.tests.unit.v3 import fakes
defaults = fakes.FakeClient(api_versions.APIVersion('3.62'))
class VolumeTypeDefaultTest(utils.TestCase):
def test_set(self):
defaults.default_types.create('4c298f16-e339-4c80-b934-6cbfcb7525a0',
'629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
defaults.assert_called(
'PUT', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7',
body={'default_type':
{'volume_type': '4c298f16-e339-4c80-b934-6cbfcb7525a0'}}
)
def test_get(self):
defaults.default_types.list('629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
defaults.assert_called(
'GET', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
def test_get_all(self):
defaults.default_types.list()
defaults.assert_called(
'GET', 'v3/default-types')
def test_unset(self):
defaults.default_types.delete('629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
defaults.assert_called(
'DELETE', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7')

View File

@ -1611,3 +1611,33 @@ class ShellTest(utils.TestCase):
def test_transfer_list_with_filters(self, command, expected): def test_transfer_list_with_filters(self, command, expected):
self.run_command('--os-volume-api-version 3.52 %s' % command) self.run_command('--os-volume-api-version 3.52 %s' % command)
self.assert_called('GET', expected) self.assert_called('GET', expected)
def test_default_type_set(self):
self.run_command('--os-volume-api-version 3.62 default-type-set '
'4c298f16-e339-4c80-b934-6cbfcb7525a0 '
'629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
body = {
'default_type':
{
'volume_type': '4c298f16-e339-4c80-b934-6cbfcb7525a0'
}
}
self.assert_called(
'PUT', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7',
body=body)
def test_default_type_list_project(self):
self.run_command('--os-volume-api-version 3.62 default-type-list '
'--project-id 629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
self.assert_called(
'GET', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
def test_default_type_list(self):
self.run_command('--os-volume-api-version 3.62 default-type-list')
self.assert_called('GET', 'v3/default-types')
def test_default_type_delete(self):
self.run_command('--os-volume-api-version 3.62 default-type-unset '
'629632e7-99d2-4c40-9ae3-106fa3b1c9b7')
self.assert_called(
'DELETE', 'v3/default-types/629632e7-99d2-4c40-9ae3-106fa3b1c9b7')

View File

@ -21,6 +21,7 @@ from cinderclient.v3 import capabilities
from cinderclient.v3 import cgsnapshots from cinderclient.v3 import cgsnapshots
from cinderclient.v3 import clusters from cinderclient.v3 import clusters
from cinderclient.v3 import consistencygroups from cinderclient.v3 import consistencygroups
from cinderclient.v3 import default_types
from cinderclient.v3 import group_snapshots from cinderclient.v3 import group_snapshots
from cinderclient.v3 import group_types from cinderclient.v3 import group_types
from cinderclient.v3 import groups from cinderclient.v3 import groups
@ -80,6 +81,7 @@ class Client(object):
volume_type_access.VolumeTypeAccessManager(self) volume_type_access.VolumeTypeAccessManager(self)
self.volume_encryption_types = \ self.volume_encryption_types = \
volume_encryption_types.VolumeEncryptionTypeManager(self) volume_encryption_types.VolumeEncryptionTypeManager(self)
self.default_types = default_types.DefaultVolumeTypeManager(self)
self.qos_specs = qos_specs.QoSSpecsManager(self) self.qos_specs = qos_specs.QoSSpecsManager(self)
self.quota_classes = quota_classes.QuotaClassSetManager(self) self.quota_classes = quota_classes.QuotaClassSetManager(self)
self.quotas = quotas.QuotaSetManager(self) self.quotas = quotas.QuotaSetManager(self)

View File

@ -0,0 +1,65 @@
# 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.
"""Default Volume Type interface."""
from cinderclient import base
class DefaultVolumeType(base.Resource):
"""Default volume types for projects."""
def __repr__(self):
return "<DefaultVolumeType: %s>" % self.project_id
class DefaultVolumeTypeManager(base.ManagerWithFind):
"""Manage :class:`DefaultVolumeType` resources."""
resource_class = DefaultVolumeType
def create(self, volume_type, project_id):
"""Creates a default volume type for a project
:param volume_type: Name or ID of the volume type
:param project_id: Project to set default type for
"""
body = {
"default_type": {
"volume_type": volume_type
}
}
return self._create_update_with_base_url(
'v3/default-types/%s' % project_id, body,
response_key='default_type')
def list(self, project_id=None):
"""List the default types."""
url = 'v3/default-types'
response_key = "default_types"
if project_id:
url += '/' + project_id
response_key = "default_type"
return self._get_all_with_base_url(url, response_key)
def delete(self, project_id):
"""Removes the default volume type for a project
:param project_id: The ID of the project to unset default for.
"""
return self._delete_with_base_url('v3/default-types/%s' % project_id)

View File

@ -2598,3 +2598,54 @@ def do_transfer_list(cs, args):
columns = ['ID', 'Volume ID', 'Name'] columns = ['ID', 'Volume ID', 'Name']
utils.print_list(transfers, columns) utils.print_list(transfers, columns)
AppendFilters.filters = [] AppendFilters.filters = []
@api_versions.wraps('3.62')
@utils.arg('volume_type',
metavar='<volume_type>',
help='Name or ID of the volume type.')
@utils.arg('project',
metavar='<project_id>',
help='ID of project for which to set default type.')
def do_default_type_set(cs, args):
"""Sets a default volume type for a project."""
volume_type = args.volume_type
project = args.project
default_type = cs.default_types.create(volume_type, project)
utils.print_dict(default_type._info)
@api_versions.wraps('3.62')
@utils.arg('--project-id',
metavar='<project_id>',
default=None,
help='ID of project for which to show the default type.')
def do_default_type_list(cs, args):
"""Lists all default volume types."""
project_id = args.project_id
default_types = cs.default_types.list(project_id)
columns = ['Volume Type ID', 'Project ID']
if project_id:
utils.print_dict(default_types._info)
else:
utils.print_list(default_types, columns)
@api_versions.wraps('3.62')
@utils.arg('project_id',
metavar='<project_id>',
nargs='+',
help='ID of project for which to unset default type.')
def do_default_type_unset(cs, args):
"""Unset default volume types."""
for project_id in args.project_id:
try:
cs.default_types.delete(project_id)
print("Default volume type for project %s has been unset "
"successfully." % (project_id))
except Exception as e:
print("Unset for default volume type for project %s failed: %s"
% (project_id, e))

View File

@ -0,0 +1,6 @@
---
features:
- |
Added support to set, get, and unset the default volume type for
projects with Block Storage API version 3.62 and higher.