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:
parent
76f2b91d9a
commit
7ee7d376a1
@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__)
|
||||
# key is a deprecated version and value is an alternative version.
|
||||
DEPRECATED_VERSIONS = {"2": "3"}
|
||||
DEPRECATED_VERSION = "2.0"
|
||||
MAX_VERSION = "3.61"
|
||||
MAX_VERSION = "3.62"
|
||||
MIN_VERSION = "3.0"
|
||||
|
||||
_SUBSTITUTIONS = {}
|
||||
|
@ -331,6 +331,26 @@ class Manager(common_base.HookableMixin):
|
||||
else:
|
||||
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)):
|
||||
"""
|
||||
|
@ -269,6 +269,12 @@ class SessionClient(adapter.LegacyJsonAdapter):
|
||||
def get_with_base_url(self, url, **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):
|
||||
|
||||
|
@ -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):
|
||||
|
||||
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)})
|
||||
|
||||
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()
|
||||
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
|
||||
#
|
||||
|
46
cinderclient/tests/unit/v3/test_default_types.py
Normal file
46
cinderclient/tests/unit/v3/test_default_types.py
Normal 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')
|
@ -1611,3 +1611,33 @@ class ShellTest(utils.TestCase):
|
||||
def test_transfer_list_with_filters(self, command, expected):
|
||||
self.run_command('--os-volume-api-version 3.52 %s' % command)
|
||||
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')
|
||||
|
@ -21,6 +21,7 @@ from cinderclient.v3 import capabilities
|
||||
from cinderclient.v3 import cgsnapshots
|
||||
from cinderclient.v3 import clusters
|
||||
from cinderclient.v3 import consistencygroups
|
||||
from cinderclient.v3 import default_types
|
||||
from cinderclient.v3 import group_snapshots
|
||||
from cinderclient.v3 import group_types
|
||||
from cinderclient.v3 import groups
|
||||
@ -80,6 +81,7 @@ class Client(object):
|
||||
volume_type_access.VolumeTypeAccessManager(self)
|
||||
self.volume_encryption_types = \
|
||||
volume_encryption_types.VolumeEncryptionTypeManager(self)
|
||||
self.default_types = default_types.DefaultVolumeTypeManager(self)
|
||||
self.qos_specs = qos_specs.QoSSpecsManager(self)
|
||||
self.quota_classes = quota_classes.QuotaClassSetManager(self)
|
||||
self.quotas = quotas.QuotaSetManager(self)
|
||||
|
65
cinderclient/v3/default_types.py
Normal file
65
cinderclient/v3/default_types.py
Normal 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)
|
@ -2598,3 +2598,54 @@ def do_transfer_list(cs, args):
|
||||
columns = ['ID', 'Volume ID', 'Name']
|
||||
utils.print_list(transfers, columns)
|
||||
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))
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user