From 9fc64a52b19cb3994df6d16af10ba5234947216f Mon Sep 17 00:00:00 2001 From: Xing Yang Date: Thu, 3 Jul 2014 22:37:40 -0400 Subject: Cinder Client for Consistency Groups This patch implements CLI commands for the Consistency Groups feature. Only snapshots for CGs will be implemented in phase 1. Change-Id: I447555fd8a92bceecf6f40be59030d65461e4cbb Implements: blueprint consistency-groups --- cinderclient/tests/v2/fakes.py | 63 ++++++++ cinderclient/tests/v2/test_cgsnapshots.py | 56 +++++++ cinderclient/tests/v2/test_consistencygroups.py | 52 ++++++ cinderclient/tests/v2/test_volumes.py | 3 +- cinderclient/v2/cgsnapshots.py | 124 ++++++++++++++ cinderclient/v2/client.py | 5 + cinderclient/v2/consistencygroups.py | 131 +++++++++++++++ cinderclient/v2/shell.py | 204 ++++++++++++++++++++++++ cinderclient/v2/volumes.py | 6 +- 9 files changed, 641 insertions(+), 3 deletions(-) create mode 100644 cinderclient/tests/v2/test_cgsnapshots.py create mode 100644 cinderclient/tests/v2/test_consistencygroups.py create mode 100644 cinderclient/v2/cgsnapshots.py create mode 100644 cinderclient/v2/consistencygroups.py diff --git a/cinderclient/tests/v2/fakes.py b/cinderclient/tests/v2/fakes.py index 5439efb..dbb50f2 100644 --- a/cinderclient/tests/v2/fakes.py +++ b/cinderclient/tests/v2/fakes.py @@ -69,6 +69,32 @@ def _stub_snapshot(**kwargs): return snapshot +def _stub_consistencygroup(**kwargs): + consistencygroup = { + "created_at": "2012-08-28T16:30:31.000000", + "description": None, + "name": "cg", + "id": "11111111-1111-1111-1111-111111111111", + "availability_zone": "myzone", + "status": "available", + } + consistencygroup.update(kwargs) + return consistencygroup + + +def _stub_cgsnapshot(**kwargs): + cgsnapshot = { + "created_at": "2012-08-28T16:30:31.000000", + "description": None, + "name": None, + "id": "11111111-1111-1111-1111-111111111111", + "status": "available", + "consistencygroup_id": "00000000-0000-0000-0000-000000000000", + } + cgsnapshot.update(kwargs) + return cgsnapshot + + def _self_href(base_uri, tenant_id, backup_id): return '%s/v2/%s/backups/%s' % (base_uri, tenant_id, backup_id) @@ -394,6 +420,43 @@ class FakeHTTPClient(base_client.HTTPClient): def delete_volumes_5678(self, **kw): return (202, {}, None) + # + # Consistencygroups + # + + def get_consistencygroups_detail(self, **kw): + return (200, {}, {"consistencygroups": [ + _stub_consistencygroup(id='1234'), + _stub_consistencygroup(id='4567')]}) + + def get_consistencygroups_1234(self, **kw): + return (200, {}, {'consistencygroup': + _stub_consistencygroup(id='1234')}) + + def post_consistencygroups(self, **kw): + return (202, {}, {'consistencygroup': {}}) + + def post_consistencygroups_1234_delete(self, **kw): + return (202, {}, {}) + + # + # Cgsnapshots + # + + def get_cgsnapshots_detail(self, **kw): + return (200, {}, {"cgsnapshots": [ + _stub_cgsnapshot(id='1234'), + _stub_cgsnapshot(id='4567')]}) + + def get_cgsnapshots_1234(self, **kw): + return (200, {}, {'cgsnapshot': _stub_cgsnapshot(id='1234')}) + + def post_cgsnapshots(self, **kw): + return (202, {}, {'cgsnapshot': {}}) + + def delete_cgsnapshots_1234(self, **kw): + return (202, {}, {}) + # # Quotas # diff --git a/cinderclient/tests/v2/test_cgsnapshots.py b/cinderclient/tests/v2/test_cgsnapshots.py new file mode 100644 index 0000000..0d5fad3 --- /dev/null +++ b/cinderclient/tests/v2/test_cgsnapshots.py @@ -0,0 +1,56 @@ +# Copyright (C) 2012 - 2014 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.tests import utils +from cinderclient.tests.v2 import fakes + + +cs = fakes.FakeClient() + + +class cgsnapshotsTest(utils.TestCase): + + def test_delete_cgsnapshot(self): + v = cs.cgsnapshots.list()[0] + v.delete() + cs.assert_called('DELETE', '/cgsnapshots/1234') + cs.cgsnapshots.delete('1234') + cs.assert_called('DELETE', '/cgsnapshots/1234') + cs.cgsnapshots.delete(v) + cs.assert_called('DELETE', '/cgsnapshots/1234') + + def test_create_cgsnapshot(self): + cs.cgsnapshots.create('cgsnap') + cs.assert_called('POST', '/cgsnapshots') + + def test_create_cgsnapshot_with_cg_id(self): + cs.cgsnapshots.create('1234') + expected = {'cgsnapshot': {'status': 'creating', + 'description': None, + 'user_id': None, + 'name': None, + 'consistencygroup_id': '1234', + 'project_id': None}} + cs.assert_called('POST', '/cgsnapshots', body=expected) + + def test_list_cgsnapshot(self): + cs.cgsnapshots.list() + cs.assert_called('GET', '/cgsnapshots/detail') + + def test_get_cgsnapshot(self): + cgsnapshot_id = '1234' + cs.cgsnapshots.get(cgsnapshot_id) + cs.assert_called('GET', '/cgsnapshots/%s' % cgsnapshot_id) diff --git a/cinderclient/tests/v2/test_consistencygroups.py b/cinderclient/tests/v2/test_consistencygroups.py new file mode 100644 index 0000000..d9b0107 --- /dev/null +++ b/cinderclient/tests/v2/test_consistencygroups.py @@ -0,0 +1,52 @@ +# Copyright (C) 2012 - 2014 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.tests import utils +from cinderclient.tests.v2 import fakes + + +cs = fakes.FakeClient() + + +class ConsistencygroupsTest(utils.TestCase): + + def test_delete_consistencygroup(self): + v = cs.consistencygroups.list()[0] + v.delete(force='True') + cs.assert_called('POST', '/consistencygroups/1234/delete') + cs.consistencygroups.delete('1234', force=True) + cs.assert_called('POST', '/consistencygroups/1234/delete') + cs.consistencygroups.delete(v, force=True) + cs.assert_called('POST', '/consistencygroups/1234/delete') + + def test_create_consistencygroup(self): + cs.consistencygroups.create('cg') + cs.assert_called('POST', '/consistencygroups') + + def test_create_consistencygroup_with_volume_types(self): + cs.consistencygroups.create('cg', volume_types='type1,type2') + expected = {'consistencygroup': {'status': 'creating', + 'description': None, + 'availability_zone': None, + 'user_id': None, + 'name': 'cg', + 'volume_types': 'type1,type2', + 'project_id': None}} + cs.assert_called('POST', '/consistencygroups', body=expected) + + def test_list_consistencygroup(self): + cs.consistencygroups.list() + cs.assert_called('GET', '/consistencygroups/detail') diff --git a/cinderclient/tests/v2/test_volumes.py b/cinderclient/tests/v2/test_volumes.py index 3b3bbcb..165742d 100644 --- a/cinderclient/tests/v2/test_volumes.py +++ b/cinderclient/tests/v2/test_volumes.py @@ -67,7 +67,8 @@ class VolumesTest(utils.TestCase): 'volume_type': None, 'project_id': None, 'metadata': {}, - 'source_replica': None}, + 'source_replica': None, + 'consistencygroup_id': None}, 'OS-SCH-HNT:scheduler_hints': 'uuid'} cs.assert_called('POST', '/volumes', body=expected) diff --git a/cinderclient/v2/cgsnapshots.py b/cinderclient/v2/cgsnapshots.py new file mode 100644 index 0000000..29513a9 --- /dev/null +++ b/cinderclient/v2/cgsnapshots.py @@ -0,0 +1,124 @@ +# Copyright (C) 2012 - 2014 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. + +"""cgsnapshot interface (v2 extension).""" + +import six +try: + from urllib import urlencode +except ImportError: + from urllib.parse import urlencode + +from cinderclient import base + + +class Cgsnapshot(base.Resource): + """A cgsnapshot is snapshot of a consistency group.""" + def __repr__(self): + return "" % self.id + + def delete(self): + """Delete this cgsnapshot.""" + self.manager.delete(self) + + def update(self, **kwargs): + """Update the name or description for this cgsnapshot.""" + self.manager.update(self, **kwargs) + + +class CgsnapshotManager(base.ManagerWithFind): + """Manage :class:`Cgsnapshot` resources.""" + resource_class = Cgsnapshot + + def create(self, consistencygroup_id, name=None, description=None, + user_id=None, + project_id=None): + """Creates a cgsnapshot. + + :param consistencygroup: Name or uuid of a consistencygroup + :param name: Name of the cgsnapshot + :param description: Description of the cgsnapshot + :param user_id: User id derived from context + :param project_id: Project id derived from context + :rtype: :class:`Cgsnapshot` + """ + + body = {'cgsnapshot': {'consistencygroup_id': consistencygroup_id, + 'name': name, + 'description': description, + 'user_id': user_id, + 'project_id': project_id, + 'status': "creating", + }} + + return self._create('/cgsnapshots', body, 'cgsnapshot') + + def get(self, cgsnapshot_id): + """Get a cgsnapshot. + + :param cgsnapshot_id: The ID of the cgsnapshot to get. + :rtype: :class:`Cgsnapshot` + """ + return self._get("/cgsnapshots/%s" % cgsnapshot_id, "cgsnapshot") + + def list(self, detailed=True, search_opts=None): + """Lists all cgsnapshots. + + :rtype: list of :class:`Cgsnapshot` + """ + if search_opts is None: + search_opts = {} + + qparams = {} + + for opt, val in six.iteritems(search_opts): + if val: + qparams[opt] = val + + query_string = "?%s" % urlencode(qparams) if qparams else "" + + detail = "" + if detailed: + detail = "/detail" + + return self._list("/cgsnapshots%s%s" % (detail, query_string), + "cgsnapshots") + + def delete(self, cgsnapshot): + """Delete a cgsnapshot. + + :param cgsnapshot: The :class:`Cgsnapshot` to delete. + """ + self._delete("/cgsnapshots/%s" % base.getid(cgsnapshot)) + + def update(self, cgsnapshot, **kwargs): + """Update the name or description for a cgsnapshot. + + :param cgsnapshot: The :class:`Cgsnapshot` to update. + """ + if not kwargs: + return + + body = {"cgsnapshot": kwargs} + + self._update("/cgsnapshots/%s" % base.getid(cgsnapshot), body) + + def _action(self, action, cgsnapshot, info=None, **kwargs): + """Perform a cgsnapshot "action." + """ + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/cgsnapshots/%s/action' % base.getid(cgsnapshot) + return self.api.client.post(url, body=body) diff --git a/cinderclient/v2/client.py b/cinderclient/v2/client.py index a4016db..143146d 100644 --- a/cinderclient/v2/client.py +++ b/cinderclient/v2/client.py @@ -15,6 +15,8 @@ from cinderclient import client from cinderclient.v2 import availability_zones +from cinderclient.v2 import cgsnapshots +from cinderclient.v2 import consistencygroups from cinderclient.v2 import limits from cinderclient.v2 import qos_specs from cinderclient.v2 import quota_classes @@ -69,6 +71,9 @@ class Client(object): self.restores = volume_backups_restore.VolumeBackupRestoreManager(self) self.transfers = volume_transfers.VolumeTransferManager(self) self.services = services.ServiceManager(self) + self.consistencygroups = consistencygroups.\ + ConsistencygroupManager(self) + self.cgsnapshots = cgsnapshots.CgsnapshotManager(self) self.availability_zones = \ availability_zones.AvailabilityZoneManager(self) diff --git a/cinderclient/v2/consistencygroups.py b/cinderclient/v2/consistencygroups.py new file mode 100644 index 0000000..cbf911b --- /dev/null +++ b/cinderclient/v2/consistencygroups.py @@ -0,0 +1,131 @@ +# Copyright (C) 2012 - 2014 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. + +"""Consistencygroup interface (v2 extension).""" + +import six +try: + from urllib import urlencode +except ImportError: + from urllib.parse import urlencode + +from cinderclient import base + + +class Consistencygroup(base.Resource): + """A Consistencygroup of volumes.""" + def __repr__(self): + return "" % self.id + + def delete(self, force='False'): + """Delete this consistencygroup.""" + self.manager.delete(self, force) + + def update(self, **kwargs): + """Update the name or description for this consistencygroup.""" + self.manager.update(self, **kwargs) + + +class ConsistencygroupManager(base.ManagerWithFind): + """Manage :class:`Consistencygroup` resources.""" + resource_class = Consistencygroup + + def create(self, name=None, description=None, + volume_types=None, user_id=None, + project_id=None, availability_zone=None): + """Creates a consistencygroup. + + :param name: Name of the ConsistencyGroup + :param description: Description of the ConsistencyGroup + :param volume_types: Types of volume + :param user_id: User id derived from context + :param project_id: Project id derived from context + :param availability_zone: Availability Zone to use + :rtype: :class:`Consistencygroup` + """ + + body = {'consistencygroup': {'name': name, + 'description': description, + 'volume_types': volume_types, + 'user_id': user_id, + 'project_id': project_id, + 'availability_zone': availability_zone, + 'status': "creating", + }} + + return self._create('/consistencygroups', body, 'consistencygroup') + + def get(self, group_id): + """Get a consistencygroup. + + :param group_id: The ID of the consistencygroup to get. + :rtype: :class:`Consistencygroup` + """ + return self._get("/consistencygroups/%s" % group_id, + "consistencygroup") + + def list(self, detailed=True, search_opts=None): + """Lists all consistencygroups. + + :rtype: list of :class:`Consistencygroup` + """ + if search_opts is None: + search_opts = {} + + qparams = {} + + for opt, val in six.iteritems(search_opts): + if val: + qparams[opt] = val + + query_string = "?%s" % urlencode(qparams) if qparams else "" + + detail = "" + if detailed: + detail = "/detail" + + return self._list("/consistencygroups%s%s" % (detail, query_string), + "consistencygroups") + + def delete(self, consistencygroup, force=False): + """Delete a consistencygroup. + + :param Consistencygroup: The :class:`Consistencygroup` to delete. + """ + body = {'consistencygroup': {'force': force}} + self.run_hooks('modify_body_for_action', body, 'consistencygroup') + url = '/consistencygroups/%s/delete' % base.getid(consistencygroup) + return self.api.client.post(url, body=body) + + def update(self, consistencygroup, **kwargs): + """Update the name or description for a consistencygroup. + + :param Consistencygroup: The :class:`Consistencygroup` to update. + """ + if not kwargs: + return + + body = {"consistencygroup": kwargs} + + self._update("/consistencygroups/%s" % base.getid(consistencygroup), + body) + + def _action(self, action, consistencygroup, info=None, **kwargs): + """Perform a consistencygroup "action." + """ + body = {action: info} + self.run_hooks('modify_body_for_action', body, **kwargs) + url = '/consistencygroups/%s/action' % base.getid(consistencygroup) + return self.api.client.post(url, body=body) diff --git a/cinderclient/v2/shell.py b/cinderclient/v2/shell.py index 2405dd5..2ff4a82 100644 --- a/cinderclient/v2/shell.py +++ b/cinderclient/v2/shell.py @@ -70,6 +70,16 @@ def _find_backup(cs, backup): return utils.find_resource(cs.backups, backup) +def _find_consistencygroup(cs, consistencygroup): + """Gets a consistencygroup by name or ID.""" + return utils.find_resource(cs.consistencygroups, consistencygroup) + + +def _find_cgsnapshot(cs, cgsnapshot): + """Gets a cgsnapshot by name or ID.""" + return utils.find_resource(cs.cgsnapshots, cgsnapshot) + + def _find_transfer(cs, transfer): """Gets a transfer by name or ID.""" return utils.find_resource(cs.transfers, transfer) @@ -240,6 +250,11 @@ class CheckSizeArgForCreate(argparse.Action): action=CheckSizeArgForCreate, help='Size of volume, in GBs. (Required unless ' 'snapshot-id/source-volid is specified).') +@utils.arg('--consisgroup-id', + metavar='', + default=None, + help='ID of a consistency group where the new volume belongs to. ' + 'Default=None.') @utils.arg('--snapshot-id', metavar='', default=None, @@ -332,6 +347,7 @@ def do_create(cs, args): #NOTE(N.S.): end of taken piece volume = cs.volumes.create(args.size, + args.consisgroup_id, args.snapshot_id, args.source_volid, args.name, @@ -1694,3 +1710,191 @@ def do_replication_promote(cs, args): def do_replication_reenable(cs, args): """Sync the secondary volume with primary for a relationship.""" utils.find_volume(cs, args.volume).reenable(args.volume) + + +@utils.arg('--all-tenants', + dest='all_tenants', + metavar='<0|1>', + nargs='?', + type=int, + const=1, + default=0, + help='Shows details for all tenants. Admin only.') +@utils.service_type('volumev2') +def do_consisgroup_list(cs, args): + """Lists all consistencygroups.""" + consistencygroups = cs.consistencygroups.list() + + columns = ['ID', 'Status', 'Name'] + utils.print_list(consistencygroups, columns) + + +@utils.arg('consistencygroup', + metavar='', + help='Name or ID of a consistency group.') +@utils.service_type('volumev2') +def do_consisgroup_show(cs, args): + """Shows details of a consistency group.""" + info = dict() + consistencygroup = _find_consistencygroup(cs, args.consistencygroup) + info.update(consistencygroup._info) + + info.pop('links', None) + utils.print_dict(info) + + +@utils.arg('--name', + metavar='', + help='Name of a consistency group.') +@utils.arg('--description', + metavar='', + default=None, + help='Description of a consistency group. Default=None.') +@utils.arg('--volume-types', + metavar='', + default=None, + help='Volume types. If not provided, default_volume_type ' + 'in cinder.conf must be specified. Default=None.') +@utils.arg('--availability-zone', + metavar='', + default=None, + help='Availability zone for volume. Default=None.') +@utils.service_type('volumev2') +def do_consisgroup_create(cs, args): + """Creates a consistency group.""" + + consistencygroup = cs.consistencygroups.create( + args.name, + args.description, + args.volume_types, + availability_zone=args.availability_zone) + + info = dict() + consistencygroup = cs.consistencygroups.get(consistencygroup.id) + info.update(consistencygroup._info) + + info.pop('links', None) + utils.print_dict(info) + + +@utils.arg('consistencygroup', + metavar='', nargs='+', + help='Name or ID of one or more consistency groups ' + 'to be deleted.') +@utils.arg('--force', + action='store_true', + help='Allows or disallows consistency groups ' + 'to be deleted. If the consistency group is empty, ' + 'it can be deleted without the force flag. ' + 'If the consistency group is not empty, the force ' + 'flag is required for it to be deleted.', + default=False) +@utils.service_type('volumev2') +def do_consisgroup_delete(cs, args): + """Removes one or more consistency groups.""" + failure_count = 0 + for consistencygroup in args.consistencygroup: + try: + _find_consistencygroup(cs, consistencygroup).delete(args.force) + except Exception as e: + failure_count += 1 + print("Delete for consistency group %s failed: %s" % + (consistencygroup, e)) + if failure_count == len(args.consistencygroup): + raise exceptions.CommandError("Unable to delete any of specified " + "consistency groups.") + + +@utils.arg('--all-tenants', + dest='all_tenants', + metavar='<0|1>', + nargs='?', + type=int, + const=1, + default=0, + help='Shows details for all tenants. Admin only.') +@utils.arg('--status', + metavar='', + default=None, + help='Filters results by a status. Default=None.') +@utils.arg('--consistencygroup-id', + metavar='', + default=None, + help='Filters results by a consistency group ID. Default=None.') +@utils.service_type('volumev2') +def do_cgsnapshot_list(cs, args): + """Lists all cgsnapshots.""" + cgsnapshots = cs.cgsnapshots.list() + + all_tenants = int(os.environ.get("ALL_TENANTS", args.all_tenants)) + + search_opts = { + 'all_tenants': all_tenants, + 'status': args.status, + 'consistencygroup_id': args.consistencygroup_id, + } + + cgsnapshots = cs.cgsnapshots.list(search_opts=search_opts) + + columns = ['ID', 'Status', 'Name'] + utils.print_list(cgsnapshots, columns) + + +@utils.arg('cgsnapshot', + metavar='', + help='Name or ID of cgsnapshot.') +@utils.service_type('volumev2') +def do_cgsnapshot_show(cs, args): + """Shows cgsnapshot details.""" + info = dict() + cgsnapshot = _find_cgsnapshot(cs, args.cgsnapshot) + info.update(cgsnapshot._info) + + info.pop('links', None) + utils.print_dict(info) + + +@utils.arg('consistencygroup', + metavar='', + help='Name or ID of a consistency group.') +@utils.arg('--name', + metavar='', + default=None, + help='Cgsnapshot name. Default=None.') +@utils.arg('--description', + metavar='', + default=None, + help='Cgsnapshot description. Default=None.') +@utils.service_type('volumev2') +def do_cgsnapshot_create(cs, args): + """Creates a cgsnapshot.""" + consistencygroup = _find_consistencygroup(cs, args.consistencygroup) + cgsnapshot = cs.cgsnapshots.create( + consistencygroup.id, + args.name, + args.description) + + info = dict() + cgsnapshot = cs.cgsnapshots.get(cgsnapshot.id) + info.update(cgsnapshot._info) + + info.pop('links', None) + utils.print_dict(info) + + +@utils.arg('cgsnapshot', + metavar='', nargs='+', + help='Name or ID of one or more cgsnapshots to be deleted.') +@utils.service_type('volumev2') +def do_cgsnapshot_delete(cs, args): + """Removes one or more cgsnapshots.""" + failure_count = 0 + for cgsnapshot in args.cgsnapshot: + try: + _find_cgsnapshot(cs, cgsnapshot).delete() + except Exception as e: + failure_count += 1 + print("Delete for cgsnapshot %s failed: %s" % (cgsnapshot, e)) + if failure_count == len(args.cgsnapshot): + raise exceptions.CommandError("Unable to delete any of specified " + "cgsnapshots.") diff --git a/cinderclient/v2/volumes.py b/cinderclient/v2/volumes.py index 98afd93..ae8ed29 100644 --- a/cinderclient/v2/volumes.py +++ b/cinderclient/v2/volumes.py @@ -165,8 +165,8 @@ class VolumeManager(base.ManagerWithFind): """Manage :class:`Volume` resources.""" resource_class = Volume - def create(self, size, snapshot_id=None, source_volid=None, - name=None, description=None, + def create(self, size, consistencygroup_id=None, snapshot_id=None, + source_volid=None, name=None, description=None, volume_type=None, user_id=None, project_id=None, availability_zone=None, metadata=None, imageRef=None, scheduler_hints=None, @@ -174,6 +174,7 @@ class VolumeManager(base.ManagerWithFind): """Creates a volume. :param size: Size of volume in GB + :param consistencygroup_id: ID of the consistencygroup :param snapshot_id: ID of the snapshot :param name: Name of the volume :param description: Description of the volume @@ -196,6 +197,7 @@ class VolumeManager(base.ManagerWithFind): volume_metadata = metadata body = {'volume': {'size': size, + 'consistencygroup_id': consistencygroup_id, 'snapshot_id': snapshot_id, 'name': name, 'description': description, -- cgit v1.2.1