Add support for os-volume-type-access extension
This change adds the ability to manage volume type access: - Create non-public volume type - List volume type access - Add a project access - Remove a project access This change also adds the is_public flag to volume type list. Note: The volume type access extension is only implemented in the Cinder API v2. DocImpact: Add volume type access extension support Implements: blueprint private-volume-types Change-Id: Ife966120d9250be8d8149cdec9c1a53405d37027
This commit is contained in:
@@ -95,6 +95,13 @@ def _stub_cgsnapshot(**kwargs):
|
|||||||
return cgsnapshot
|
return cgsnapshot
|
||||||
|
|
||||||
|
|
||||||
|
def _stub_type_access(**kwargs):
|
||||||
|
access = {'volume_type_id': '11111111-1111-1111-1111-111111111111',
|
||||||
|
'project_id': '00000000-0000-0000-0000-000000000000'}
|
||||||
|
access.update(kwargs)
|
||||||
|
return access
|
||||||
|
|
||||||
|
|
||||||
def _self_href(base_uri, tenant_id, backup_id):
|
def _self_href(base_uri, tenant_id, backup_id):
|
||||||
return '%s/v2/%s/backups/%s' % (base_uri, tenant_id, backup_id)
|
return '%s/v2/%s/backups/%s' % (base_uri, tenant_id, backup_id)
|
||||||
|
|
||||||
@@ -559,25 +566,37 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
#
|
#
|
||||||
# VolumeTypes
|
# VolumeTypes
|
||||||
#
|
#
|
||||||
|
|
||||||
def get_types(self, **kw):
|
def get_types(self, **kw):
|
||||||
return (200, {}, {
|
return (200, {}, {
|
||||||
'volume_types': [{'id': 1,
|
'volume_types': [{'id': 1,
|
||||||
'name': 'test-type-1',
|
'name': 'test-type-1',
|
||||||
|
'description': 'test_type-1-desc',
|
||||||
'extra_specs': {}},
|
'extra_specs': {}},
|
||||||
{'id': 2,
|
{'id': 2,
|
||||||
'name': 'test-type-2',
|
'name': 'test-type-2',
|
||||||
|
'description': 'test_type-2-desc',
|
||||||
'extra_specs': {}}]})
|
'extra_specs': {}}]})
|
||||||
|
|
||||||
def get_types_1(self, **kw):
|
def get_types_1(self, **kw):
|
||||||
return (200, {}, {'volume_type': {'id': 1,
|
return (200, {}, {'volume_type': {'id': 1,
|
||||||
'name': 'test-type-1',
|
'name': 'test-type-1',
|
||||||
|
'description': 'test_type-1-desc',
|
||||||
'extra_specs': {}}})
|
'extra_specs': {}}})
|
||||||
|
|
||||||
def get_types_2(self, **kw):
|
def get_types_2(self, **kw):
|
||||||
return (200, {}, {'volume_type': {'id': 2,
|
return (200, {}, {'volume_type': {'id': 2,
|
||||||
'name': 'test-type-2',
|
'name': 'test-type-2',
|
||||||
|
'description': 'test_type-2-desc',
|
||||||
'extra_specs': {}}})
|
'extra_specs': {}}})
|
||||||
|
|
||||||
|
def get_types_3(self, **kw):
|
||||||
|
return (200, {}, {'volume_type': {'id': 3,
|
||||||
|
'name': 'test-type-3',
|
||||||
|
'description': 'test_type-3-desc',
|
||||||
|
'extra_specs': {},
|
||||||
|
'os-volume-type-access:is_public': False}})
|
||||||
|
|
||||||
def get_types_default(self, **kw):
|
def get_types_default(self, **kw):
|
||||||
return self.get_types_1()
|
return self.get_types_1()
|
||||||
|
|
||||||
@@ -587,6 +606,19 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
'description': 'test_type-3-desc',
|
'description': 'test_type-3-desc',
|
||||||
'extra_specs': {}}})
|
'extra_specs': {}}})
|
||||||
|
|
||||||
|
def post_types_3_action(self, body, **kw):
|
||||||
|
_body = None
|
||||||
|
resp = 202
|
||||||
|
assert len(list(body)) == 1
|
||||||
|
action = list(body)[0]
|
||||||
|
if action == 'addProjectAccess':
|
||||||
|
assert 'project' in body['addProjectAccess']
|
||||||
|
elif action == 'removeProjectAccess':
|
||||||
|
assert 'project' in body['removeProjectAccess']
|
||||||
|
else:
|
||||||
|
raise AssertionError('Unexpected action: %s' % action)
|
||||||
|
return (resp, {}, _body)
|
||||||
|
|
||||||
def post_types_1_extra_specs(self, body, **kw):
|
def post_types_1_extra_specs(self, body, **kw):
|
||||||
assert list(body) == ['extra_specs']
|
assert list(body) == ['extra_specs']
|
||||||
return (200, {}, {'extra_specs': {'k': 'v'}})
|
return (200, {}, {'extra_specs': {'k': 'v'}})
|
||||||
@@ -600,6 +632,15 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
def put_types_1(self, **kw):
|
def put_types_1(self, **kw):
|
||||||
return self.get_types_1()
|
return self.get_types_1()
|
||||||
|
|
||||||
|
#
|
||||||
|
# VolumeAccess
|
||||||
|
#
|
||||||
|
|
||||||
|
def get_types_3_os_volume_type_access(self, **kw):
|
||||||
|
return (200, {}, {'volume_type_access': [
|
||||||
|
_stub_type_access()
|
||||||
|
]})
|
||||||
|
|
||||||
#
|
#
|
||||||
# VolumeEncryptionTypes
|
# VolumeEncryptionTypes
|
||||||
#
|
#
|
||||||
|
@@ -298,6 +298,55 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assert_called_anytime('POST', '/snapshots/5678/action',
|
self.assert_called_anytime('POST', '/snapshots/5678/action',
|
||||||
body=expected)
|
body=expected)
|
||||||
|
|
||||||
|
def test_type_list(self):
|
||||||
|
self.run_command('type-list')
|
||||||
|
self.assert_called_anytime('GET', '/types')
|
||||||
|
|
||||||
|
def test_type_list_all(self):
|
||||||
|
self.run_command('type-list --all')
|
||||||
|
self.assert_called_anytime('GET', '/types?is_public=None')
|
||||||
|
|
||||||
|
def test_type_create(self):
|
||||||
|
self.run_command('type-create test-type-1')
|
||||||
|
self.assert_called('POST', '/types')
|
||||||
|
|
||||||
|
def test_type_create_public(self):
|
||||||
|
expected = {'volume_type': {'name': 'test-type-1',
|
||||||
|
'description': 'test_type-1-desc',
|
||||||
|
'os-volume-type-access:is_public': True}}
|
||||||
|
self.run_command('type-create test-type-1 '
|
||||||
|
'--description=test_type-1-desc '
|
||||||
|
'--is-public=True')
|
||||||
|
self.assert_called('POST', '/types', body=expected)
|
||||||
|
|
||||||
|
def test_type_create_private(self):
|
||||||
|
expected = {'volume_type': {'name': 'test-type-3',
|
||||||
|
'description': 'test_type-3-desc',
|
||||||
|
'os-volume-type-access:is_public': False}}
|
||||||
|
self.run_command('type-create test-type-3 '
|
||||||
|
'--description=test_type-3-desc '
|
||||||
|
'--is-public=False')
|
||||||
|
self.assert_called('POST', '/types', body=expected)
|
||||||
|
|
||||||
|
def test_type_access_list(self):
|
||||||
|
self.run_command('type-access-list --volume-type 3')
|
||||||
|
self.assert_called('GET', '/types/3/os-volume-type-access')
|
||||||
|
|
||||||
|
def test_type_access_add_project(self):
|
||||||
|
expected = {'addProjectAccess': {'project': '101'}}
|
||||||
|
self.run_command('type-access-add --volume-type 3 --project-id 101')
|
||||||
|
self.assert_called_anytime('GET', '/types/3')
|
||||||
|
self.assert_called('POST', '/types/3/action',
|
||||||
|
body=expected)
|
||||||
|
|
||||||
|
def test_type_access_remove_project(self):
|
||||||
|
expected = {'removeProjectAccess': {'project': '101'}}
|
||||||
|
self.run_command('type-access-remove '
|
||||||
|
'--volume-type 3 --project-id 101')
|
||||||
|
self.assert_called_anytime('GET', '/types/3')
|
||||||
|
self.assert_called('POST', '/types/3/action',
|
||||||
|
body=expected)
|
||||||
|
|
||||||
def test_encryption_type_list(self):
|
def test_encryption_type_list(self):
|
||||||
"""
|
"""
|
||||||
Test encryption-type-list shell command.
|
Test encryption-type-list shell command.
|
||||||
|
42
cinderclient/tests/v2/test_type_access.py
Normal file
42
cinderclient/tests/v2/test_type_access.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Copyright (c) 2013 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.v2 import volume_type_access
|
||||||
|
from cinderclient.tests import utils
|
||||||
|
from cinderclient.tests.v2 import fakes
|
||||||
|
|
||||||
|
cs = fakes.FakeClient()
|
||||||
|
|
||||||
|
PROJECT_UUID = '11111111-1111-1111-111111111111'
|
||||||
|
|
||||||
|
|
||||||
|
class TypeAccessTest(utils.TestCase):
|
||||||
|
|
||||||
|
def test_list(self):
|
||||||
|
access = cs.volume_type_access.list(volume_type='3')
|
||||||
|
cs.assert_called('GET', '/types/3/os-volume-type-access')
|
||||||
|
for a in access:
|
||||||
|
self.assertTrue(isinstance(a, volume_type_access.VolumeTypeAccess))
|
||||||
|
|
||||||
|
def test_add_project_access(self):
|
||||||
|
cs.volume_type_access.add_project_access('3', PROJECT_UUID)
|
||||||
|
cs.assert_called('POST', '/types/3/action',
|
||||||
|
{'addProjectAccess': {'project': PROJECT_UUID}})
|
||||||
|
|
||||||
|
def test_remove_project_access(self):
|
||||||
|
cs.volume_type_access.remove_project_access('3', PROJECT_UUID)
|
||||||
|
cs.assert_called('POST', '/types/3/action',
|
||||||
|
{'removeProjectAccess': {'project': PROJECT_UUID}})
|
@@ -22,15 +22,35 @@ cs = fakes.FakeClient()
|
|||||||
|
|
||||||
|
|
||||||
class TypesTest(utils.TestCase):
|
class TypesTest(utils.TestCase):
|
||||||
|
|
||||||
def test_list_types(self):
|
def test_list_types(self):
|
||||||
tl = cs.volume_types.list()
|
tl = cs.volume_types.list()
|
||||||
cs.assert_called('GET', '/types')
|
cs.assert_called('GET', '/types')
|
||||||
for t in tl:
|
for t in tl:
|
||||||
self.assertIsInstance(t, volume_types.VolumeType)
|
self.assertIsInstance(t, volume_types.VolumeType)
|
||||||
|
|
||||||
|
def test_list_types_not_public(self):
|
||||||
|
cs.volume_types.list(is_public=None)
|
||||||
|
cs.assert_called('GET', '/types?is_public=None')
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
t = cs.volume_types.create('test-type-3')
|
t = cs.volume_types.create('test-type-3', 'test-type-3-desc')
|
||||||
cs.assert_called('POST', '/types')
|
cs.assert_called('POST', '/types',
|
||||||
|
{'volume_type': {
|
||||||
|
'name': 'test-type-3',
|
||||||
|
'description': 'test-type-3-desc',
|
||||||
|
'os-volume-type-access:is_public': True
|
||||||
|
}})
|
||||||
|
self.assertIsInstance(t, volume_types.VolumeType)
|
||||||
|
|
||||||
|
def test_create_non_public(self):
|
||||||
|
t = cs.volume_types.create('test-type-3', 'test-type-3-desc', False)
|
||||||
|
cs.assert_called('POST', '/types',
|
||||||
|
{'volume_type': {
|
||||||
|
'name': 'test-type-3',
|
||||||
|
'description': 'test-type-3-desc',
|
||||||
|
'os-volume-type-access:is_public': False
|
||||||
|
}})
|
||||||
self.assertIsInstance(t, volume_types.VolumeType)
|
self.assertIsInstance(t, volume_types.VolumeType)
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
|
@@ -25,6 +25,7 @@ from cinderclient.v2 import services
|
|||||||
from cinderclient.v2 import volumes
|
from cinderclient.v2 import volumes
|
||||||
from cinderclient.v2 import volume_snapshots
|
from cinderclient.v2 import volume_snapshots
|
||||||
from cinderclient.v2 import volume_types
|
from cinderclient.v2 import volume_types
|
||||||
|
from cinderclient.v2 import volume_type_access
|
||||||
from cinderclient.v2 import volume_encryption_types
|
from cinderclient.v2 import volume_encryption_types
|
||||||
from cinderclient.v2 import volume_backups
|
from cinderclient.v2 import volume_backups
|
||||||
from cinderclient.v2 import volume_backups_restore
|
from cinderclient.v2 import volume_backups_restore
|
||||||
@@ -61,6 +62,8 @@ class Client(object):
|
|||||||
self.volumes = volumes.VolumeManager(self)
|
self.volumes = volumes.VolumeManager(self)
|
||||||
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
||||||
self.volume_types = volume_types.VolumeTypeManager(self)
|
self.volume_types = volume_types.VolumeTypeManager(self)
|
||||||
|
self.volume_type_access = \
|
||||||
|
volume_type_access.VolumeTypeAccessManager(self)
|
||||||
self.volume_encryption_types = \
|
self.volume_encryption_types = \
|
||||||
volume_encryption_types.VolumeEncryptionTypeManager(self)
|
volume_encryption_types.VolumeEncryptionTypeManager(self)
|
||||||
self.qos_specs = qos_specs.QoSSpecsManager(self)
|
self.qos_specs = qos_specs.QoSSpecsManager(self)
|
||||||
|
@@ -696,13 +696,21 @@ def do_snapshot_reset_state(cs, args):
|
|||||||
|
|
||||||
|
|
||||||
def _print_volume_type_list(vtypes):
|
def _print_volume_type_list(vtypes):
|
||||||
utils.print_list(vtypes, ['ID', 'Name', 'Description'])
|
utils.print_list(vtypes, ['ID', 'Name', 'Description', 'Is_Public'])
|
||||||
|
|
||||||
|
|
||||||
@utils.service_type('volumev2')
|
@utils.service_type('volumev2')
|
||||||
|
@utils.arg('--all',
|
||||||
|
dest='all',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help='Display all volume types (Admin only).')
|
||||||
def do_type_list(cs, args):
|
def do_type_list(cs, args):
|
||||||
"""Lists available 'volume types'."""
|
"""Lists available 'volume types'."""
|
||||||
vtypes = cs.volume_types.list()
|
if args.all:
|
||||||
|
vtypes = cs.volume_types.list(is_public=None)
|
||||||
|
else:
|
||||||
|
vtypes = cs.volume_types.list()
|
||||||
_print_volume_type_list(vtypes)
|
_print_volume_type_list(vtypes)
|
||||||
|
|
||||||
|
|
||||||
@@ -739,10 +747,15 @@ def do_extra_specs_list(cs, args):
|
|||||||
@utils.arg('--description',
|
@utils.arg('--description',
|
||||||
metavar='<description>',
|
metavar='<description>',
|
||||||
help="Description of new volume type.")
|
help="Description of new volume type.")
|
||||||
|
@utils.arg('--is-public',
|
||||||
|
metavar='<is-public>',
|
||||||
|
help="Make type accessible to the public (default true).",
|
||||||
|
default=True)
|
||||||
@utils.service_type('volumev2')
|
@utils.service_type('volumev2')
|
||||||
def do_type_create(cs, args):
|
def do_type_create(cs, args):
|
||||||
"""Creates a volume type."""
|
"""Creates a volume type."""
|
||||||
vtype = cs.volume_types.create(args.name, args.description)
|
is_public = strutils.bool_from_string(args.is_public)
|
||||||
|
vtype = cs.volume_types.create(args.name, args.description, is_public)
|
||||||
_print_volume_type_list([vtype])
|
_print_volume_type_list([vtype])
|
||||||
|
|
||||||
|
|
||||||
@@ -780,6 +793,45 @@ def do_type_key(cs, args):
|
|||||||
vtype.unset_keys(list(keypair))
|
vtype.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('volumev2')
|
||||||
|
def do_type_access_list(cs, args):
|
||||||
|
"""Print access information about the given volume type."""
|
||||||
|
volume_type = _find_volume_type(cs, args.volume_type)
|
||||||
|
if volume_type.is_public:
|
||||||
|
raise exceptions.CommandError("Failed to get access list "
|
||||||
|
"for public volume type.")
|
||||||
|
access_list = cs.volume_type_access.list(volume_type)
|
||||||
|
|
||||||
|
columns = ['Volume_type_ID', 'Project_ID']
|
||||||
|
utils.print_list(access_list, columns)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('--volume-type', metavar='<volume_type>', required=True,
|
||||||
|
help="Volume type name or ID to add access for the given project.")
|
||||||
|
@utils.arg('--project-id', metavar='<project_id>', required=True,
|
||||||
|
help='Project ID to add volume type access for.')
|
||||||
|
@utils.service_type('volumev2')
|
||||||
|
def do_type_access_add(cs, args):
|
||||||
|
"""Adds volume type access for the given project."""
|
||||||
|
vtype = _find_volume_type(cs, args.volume_type)
|
||||||
|
cs.volume_type_access.add_project_access(vtype, args.project_id)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('--volume-type', metavar='<volume_type>', required=True,
|
||||||
|
help=('Volume type name or ID to remove access '
|
||||||
|
'for the given project.'))
|
||||||
|
@utils.arg('--project-id', metavar='<project_id>', required=True,
|
||||||
|
help='Project ID to remove volume type access for.')
|
||||||
|
@utils.service_type('volumev2')
|
||||||
|
def do_type_access_remove(cs, args):
|
||||||
|
"""Removes volume type access for the given project."""
|
||||||
|
vtype = _find_volume_type(cs, args.volume_type)
|
||||||
|
cs.volume_type_access.remove_project_access(
|
||||||
|
vtype, args.project_id)
|
||||||
|
|
||||||
|
|
||||||
@utils.service_type('volumev2')
|
@utils.service_type('volumev2')
|
||||||
def do_endpoints(cs, args):
|
def do_endpoints(cs, args):
|
||||||
"""Discovers endpoints registered by authentication service."""
|
"""Discovers endpoints registered by authentication service."""
|
||||||
|
51
cinderclient/v2/volume_type_access.py
Normal file
51
cinderclient/v2/volume_type_access.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Volume type access interface."""
|
||||||
|
|
||||||
|
from cinderclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTypeAccess(base.Resource):
|
||||||
|
def __repr__(self):
|
||||||
|
return "<VolumeTypeAccess: %s>" % self.project_id
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeTypeAccessManager(base.ManagerWithFind):
|
||||||
|
"""
|
||||||
|
Manage :class:`VolumeTypeAccess` resources.
|
||||||
|
"""
|
||||||
|
resource_class = VolumeTypeAccess
|
||||||
|
|
||||||
|
def list(self, volume_type):
|
||||||
|
return self._list(
|
||||||
|
'/types/%s/os-volume-type-access' % base.getid(volume_type),
|
||||||
|
'volume_type_access')
|
||||||
|
|
||||||
|
def add_project_access(self, volume_type, project):
|
||||||
|
"""Add a project to the given volume type access list."""
|
||||||
|
info = {'project': project}
|
||||||
|
self._action('addProjectAccess', volume_type, info)
|
||||||
|
|
||||||
|
def remove_project_access(self, volume_type, project):
|
||||||
|
"""Remove a project from the given volume type access list."""
|
||||||
|
info = {'project': project}
|
||||||
|
self._action('removeProjectAccess', volume_type, info)
|
||||||
|
|
||||||
|
def _action(self, action, volume_type, info, **kwargs):
|
||||||
|
"""Perform a volume type action."""
|
||||||
|
body = {action: info}
|
||||||
|
self.run_hooks('modify_body_for_action', body, **kwargs)
|
||||||
|
url = '/types/%s/action' % base.getid(volume_type)
|
||||||
|
return self.api.client.post(url, body=body)
|
@@ -24,6 +24,13 @@ class VolumeType(base.Resource):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<VolumeType: %s>" % self.name
|
return "<VolumeType: %s>" % self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_public(self):
|
||||||
|
"""
|
||||||
|
Provide a user-friendly accessor to os-volume-type-access:is_public
|
||||||
|
"""
|
||||||
|
return self._info.get("os-volume-type-access:is_public", 'N/A')
|
||||||
|
|
||||||
def get_keys(self):
|
def get_keys(self):
|
||||||
"""Get extra specs from a volume type.
|
"""Get extra specs from a volume type.
|
||||||
|
|
||||||
@@ -70,12 +77,15 @@ class VolumeTypeManager(base.ManagerWithFind):
|
|||||||
"""Manage :class:`VolumeType` resources."""
|
"""Manage :class:`VolumeType` resources."""
|
||||||
resource_class = VolumeType
|
resource_class = VolumeType
|
||||||
|
|
||||||
def list(self, search_opts=None):
|
def list(self, search_opts=None, is_public=True):
|
||||||
"""Lists all volume types.
|
"""Lists all volume types.
|
||||||
|
|
||||||
:rtype: list of :class:`VolumeType`.
|
:rtype: list of :class:`VolumeType`.
|
||||||
"""
|
"""
|
||||||
return self._list("/types", "volume_types")
|
query_string = ''
|
||||||
|
if not is_public:
|
||||||
|
query_string = '?is_public=%s' % is_public
|
||||||
|
return self._list("/types%s" % (query_string), "volume_types")
|
||||||
|
|
||||||
def get(self, volume_type):
|
def get(self, volume_type):
|
||||||
"""Get a specific volume type.
|
"""Get a specific volume type.
|
||||||
@@ -99,18 +109,20 @@ class VolumeTypeManager(base.ManagerWithFind):
|
|||||||
"""
|
"""
|
||||||
self._delete("/types/%s" % base.getid(volume_type))
|
self._delete("/types/%s" % base.getid(volume_type))
|
||||||
|
|
||||||
def create(self, name, description=None):
|
def create(self, name, description=None, is_public=True):
|
||||||
"""Creates a volume type.
|
"""Creates a volume type.
|
||||||
|
|
||||||
:param name: Descriptive name of the volume type
|
:param name: Descriptive name of the volume type
|
||||||
:param description: Description of the the volume type
|
:param description: Description of the the volume type
|
||||||
|
:param is_public: Volume type visibility
|
||||||
:rtype: :class:`VolumeType`
|
:rtype: :class:`VolumeType`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
"volume_type": {
|
"volume_type": {
|
||||||
"name": name,
|
"name": name,
|
||||||
"description": description
|
"description": description,
|
||||||
|
"os-volume-type-access:is_public": is_public,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user