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
This commit is contained in:
@@ -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
|
||||
#
|
||||
|
56
cinderclient/tests/v2/test_cgsnapshots.py
Normal file
56
cinderclient/tests/v2/test_cgsnapshots.py
Normal file
@@ -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)
|
52
cinderclient/tests/v2/test_consistencygroups.py
Normal file
52
cinderclient/tests/v2/test_consistencygroups.py
Normal file
@@ -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')
|
@@ -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)
|
||||
|
||||
|
124
cinderclient/v2/cgsnapshots.py
Normal file
124
cinderclient/v2/cgsnapshots.py
Normal file
@@ -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 "<cgsnapshot: %s>" % 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)
|
@@ -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)
|
||||
|
||||
|
131
cinderclient/v2/consistencygroups.py
Normal file
131
cinderclient/v2/consistencygroups.py
Normal file
@@ -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 "<Consistencygroup: %s>" % 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)
|
@@ -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='<consistencygroup-id>',
|
||||
default=None,
|
||||
help='ID of a consistency group where the new volume belongs to. '
|
||||
'Default=None.')
|
||||
@utils.arg('--snapshot-id',
|
||||
metavar='<snapshot-id>',
|
||||
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='<consistencygroup>',
|
||||
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='<name>',
|
||||
help='Name of a consistency group.')
|
||||
@utils.arg('--description',
|
||||
metavar='<description>',
|
||||
default=None,
|
||||
help='Description of a consistency group. Default=None.')
|
||||
@utils.arg('--volume-types',
|
||||
metavar='<volume-types>',
|
||||
default=None,
|
||||
help='Volume types. If not provided, default_volume_type '
|
||||
'in cinder.conf must be specified. Default=None.')
|
||||
@utils.arg('--availability-zone',
|
||||
metavar='<availability-zone>',
|
||||
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='<consistencygroup>', 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='<status>',
|
||||
default=None,
|
||||
help='Filters results by a status. Default=None.')
|
||||
@utils.arg('--consistencygroup-id',
|
||||
metavar='<consistencygroup_id>',
|
||||
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='<cgsnapshot>',
|
||||
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='<consistencygroup>',
|
||||
help='Name or ID of a consistency group.')
|
||||
@utils.arg('--name',
|
||||
metavar='<name>',
|
||||
default=None,
|
||||
help='Cgsnapshot name. Default=None.')
|
||||
@utils.arg('--description',
|
||||
metavar='<description>',
|
||||
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='<cgsnapshot>', 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.")
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user