Merge "XtremIO volume driver consistency group support"

This commit is contained in:
Jenkins 2015-07-25 04:25:48 +00:00 committed by Gerrit Code Review
commit 86baaff849
2 changed files with 310 additions and 96 deletions

View File

@ -13,11 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from cinder import exception
from cinder import test
from cinder.tests.unit import fake_snapshot
from cinder.volume.drivers.emc import xtremio
@ -25,55 +25,67 @@ typ2id = {'volumes': 'vol-id',
'snapshots': 'vol-id',
'initiators': 'initiator-id',
'initiator-groups': 'ig-id',
'lun-maps': 'mapping-id'}
'lun-maps': 'mapping-id',
'consistency-groups': 'cg-id',
'consistency-group-volumes': 'cg-vol-id',
}
xms_data = {'xms': {1: {'version': '4.0.0'}},
'clusters': {'cluster1':
{'name': 'cluster1',
'sys-sw-version': "3.0.0-devel_ba23ee5381eeab73",
'ud-ssd-space': '8146708710',
'ud-ssd-space-in-use': '708710',
'vol-size': '29884416',
'chap-authentication-mode': 'disabled',
'chap-discovery-mode': 'disabled',
"index": 1},
1: {'name': 'cluster1',
'sys-sw-version': "3.0.0-devel_ba23ee5381eeab73",
'clusters': {1: {'name': 'brick1',
'sys-sw-version': "4.0.0-devel_ba23ee5381eeab73",
'ud-ssd-space': '8146708710',
'ud-ssd-space-in-use': '708710',
'vol-size': '29884416',
'chap-authentication-mode': 'disabled',
'chap-discovery-mode': 'disabled',
"index": 1}},
'target-groups': {'Default': {"index": 1, }},
"index": 1,
},
},
'target-groups': {'Default': {"index": 1, },
},
'iscsi-portals': {'10.205.68.5/16':
{"port-address":
"iqn.2008-05.com.xtremio:001e67939c34",
"ip-port": 3260,
"ip-addr": "10.205.68.5/16",
"name": "10.205.68.5/16",
"index": 1}},
"index": 1,
},
},
'targets': {'X1-SC2-fc1': {'index': 1, "name": "X1-SC2-fc1",
"port-address":
"21:00:00:24:ff:57:b2:36",
'port-state': 'up'},
'port-state': 'up',
},
'X1-SC2-fc2': {'index': 2, "name": "X1-SC2-fc2",
"port-address":
"21:00:00:24:ff:57:b2:55",
'port-state': 'up'}
'port-state': 'up',
}
},
'volumes': {},
'initiator-groups': {},
'initiators': {},
'lun-maps': {},
'consistency-groups': {},
'consistency-group-volumes': {},
}
def get_xms_obj_by_name(typ, name):
for item in xms_data[typ].values():
if 'name' in item and item['name'] == name:
return item
raise exception.NotFound()
def clean_xms_data():
xms_data['volumes'] = {}
xms_data['initiator-groups'] = {}
xms_data['initiators'] = {}
xms_data['lun-maps'] = {}
xms_data['consistency-group-volumes'] = {}
xms_data['consistency-groups'] = {}
def fix_data(data, object_type):
@ -88,7 +100,7 @@ def fix_data(data, object_type):
d[typ2id[object_type]] = ["a91e8c81c2d14ae4865187ce4f866f8a",
d.get('name'),
len(xms_data[object_type]) + 1]
len(xms_data.get(object_type, [])) + 1]
d['index'] = len(xms_data[object_type]) + 1
return d
@ -99,24 +111,30 @@ def get_xms_obj_key(data):
return key
def get_obj(typ, name, idx):
if name:
return {"content": get_xms_obj_by_name(typ, name)}
elif idx:
if idx not in xms_data.get(typ, {}):
raise exception.NotFound()
return {"content": xms_data[typ][idx]}
def xms_request(object_type='volumes', request_typ='GET', data=None,
name=None, idx=None):
name=None, idx=None, ver='v1'):
if object_type == 'snapshots':
object_type = 'volumes'
obj_key = name if name else idx
try:
res = xms_data[object_type]
except KeyError:
raise exception.VolumeDriverException
if request_typ == 'GET':
try:
res = xms_data[object_type]
except KeyError:
raise exception.VolumeDriverException
if name or idx:
if obj_key not in res:
raise exception.NotFound()
return {"content": res[obj_key]}
return get_obj(object_type, name, idx)
else:
if data and data.get('full') == 1:
return {object_type: res.values()}
return {object_type: list(res.values())}
else:
return {object_type: [{"href": "/%s/%d" % (object_type,
obj['index']),
@ -124,41 +142,45 @@ def xms_request(object_type='volumes', request_typ='GET', data=None,
for obj in res.values()]}
elif request_typ == 'POST':
data = fix_data(data, object_type)
data['index'] = len(xms_data[object_type]) + 1
xms_data[object_type][data['index']] = data
# find the name key
name_key = get_xms_obj_key(data)
if object_type == 'lun-maps':
data['ig-name'] = data['ig-id']
if name_key:
if data[name_key] in xms_data[object_type]:
try:
if name_key and get_xms_obj_by_name(object_type, data[name_key]):
raise (exception
.VolumeBackendAPIException
('Volume by this name already exists'))
xms_data[object_type][data[name_key]] = data
except exception.NotFound:
pass
data['index'] = len(xms_data[object_type]) + 1
xms_data[object_type][data['index']] = data
# find the name key
if name_key:
data['name'] = data[name_key]
if object_type == 'lun-maps':
data['ig-name'] = data['ig-id']
return {"links": [{"href": "/%s/%d" %
(object_type, data[typ2id[object_type]][2])}]}
elif request_typ == 'DELETE':
if obj_key in xms_data[object_type]:
data = xms_data[object_type][obj_key]
if object_type == 'consistency-group-volumes':
data = [cgv for cgv in
xms_data['consistency-group-volumes'].values()
if cgv['vol-id'] == data['vol-id']
and cgv['cg-id'] == data['cg-id']][0]
else:
data = get_obj(object_type, name, idx)['content']
if data:
del xms_data[object_type][data['index']]
del xms_data[object_type][data[typ2id[object_type]][1]]
else:
raise exception.NotFound()
elif request_typ == 'PUT':
if obj_key in xms_data[object_type]:
obj = xms_data[object_type][obj_key]
obj.update(data)
key = get_xms_obj_key(data)
if key:
xms_data[object_type][data[key]] = obj
else:
raise exception.NotFound()
obj = get_obj(object_type, name, idx)['content']
data = fix_data(data, object_type)
del data['index']
obj.update(data)
def xms_bad_request(object_type='volumes', request_typ='GET', data=None,
name=None, idx=None):
name=None, idx=None, ver='v1'):
if request_typ == 'GET':
raise exception.NotFound()
elif request_typ == 'POST':
@ -167,7 +189,7 @@ def xms_bad_request(object_type='volumes', request_typ='GET', data=None,
def xms_failed_rename_snapshot_request(object_type='volumes',
request_typ='GET', data=None,
name=None, idx=None):
name=None, idx=None, ver='v1'):
if request_typ == 'POST':
xms_data['volumes'][27] = {}
return {
@ -176,7 +198,7 @@ def xms_failed_rename_snapshot_request(object_type='volumes',
"href": "https://host/api/json/v2/types/snapshots/27",
"rel": "self"}]}
elif request_typ == 'PUT':
raise exception.VolumeBackendAPIException(msg='Failed to delete')
raise exception.VolumeBackendAPIException(data='Failed to delete')
elif request_typ == 'DELETE':
del xms_data['volumes'][27]
@ -192,7 +214,8 @@ class CommonData(object):
'initiator': 'iqn.1993-08.org.debian:01:222',
'wwpns': ["123456789012345", "123456789054321"],
'wwnns': ["223456789012345", "223456789054321"],
'host': 'fakehost'}
'host': 'fakehost',
}
test_volume = {'name': 'vol1',
'size': 1,
@ -202,14 +225,20 @@ class CommonData(object):
'project_id': 'project',
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None}
'volume_type_id': None,
'consistencygroup_id':
'192eb39b-6c2f-420c-bae3-3cfd117f0345',
}
test_snapshot = D()
test_snapshot.update({'name': 'snapshot1',
'size': 1,
'id': '192eb39b-6c2f-420c-bae3-3cfd117f0002',
'volume_name': 'vol-vol1',
'volume_id': '192eb39b-6c2f-420c-bae3-3cfd117f0001',
'project_id': 'project'})
'project_id': 'project',
'consistencygroup_id':
'192eb39b-6c2f-420c-bae3-3cfd117f0345',
})
test_snapshot.__dict__.update(test_snapshot)
test_volume2 = {'name': 'vol2',
'size': 1,
@ -219,7 +248,10 @@ class CommonData(object):
'project_id': 'project',
'display_name': 'vol2',
'display_description': 'test volume 2',
'volume_type_id': None}
'volume_type_id': None,
'consistencygroup_id':
'192eb39b-6c2f-420c-bae3-3cfd117f0345',
}
test_clone = {'name': 'clone1',
'size': 1,
'volume_name': 'vol3',
@ -228,30 +260,49 @@ class CommonData(object):
'project_id': 'project',
'display_name': 'clone1',
'display_description': 'volume created from snapshot',
'volume_type_id': None}
'volume_type_id': None,
'consistencygroup_id':
'192eb39b-6c2f-420c-bae3-3cfd117f0345',
}
unmanaged1 = {'id': 'unmanaged1',
'name': 'unmanaged1',
'size': 3}
'size': 3,
}
context = {'user': 'admin', }
group = {'id': '192eb39b-6c2f-420c-bae3-3cfd117f0345',
'name': 'cg1',
'status': 'OK',
}
cgsnapshot = mock.Mock(id='192eb39b-6c2f-420c-bae3-3cfd117f9876',
consistencygroup_id=group['id'])
def cgsnap_getitem(self, val):
return self.__dict__[val]
cgsnapshot.__getitem__ = cgsnap_getitem
@mock.patch('cinder.volume.drivers.emc.xtremio.XtremIOClient.req')
class EMCXIODriverISCSITestCase(test.TestCase):
def setUp(self):
super(EMCXIODriverISCSITestCase, self).setUp()
clean_xms_data()
configuration = mock.Mock()
configuration.san_login = ''
configuration.san_password = ''
configuration.san_ip = ''
configuration.xtremio_cluster_name = ''
configuration.xtremio_provisioning_factor = 20.0
config = mock.Mock()
config.san_login = ''
config.san_password = ''
config.san_ip = ''
config.xtremio_cluster_name = 'brick1'
config.xtremio_provisioning_factor = 20.0
def safe_get(key):
getattr(configuration, key)
configuration.safe_get = safe_get
self.driver = xtremio.XtremIOISCSIDriver(configuration=configuration)
getattr(config, key)
config.safe_get = safe_get
self.driver = xtremio.XtremIOISCSIDriver(configuration=config)
self.driver.client = xtremio.XtremIOClient4(config,
config
.xtremio_cluster_name)
self.data = CommonData()
def test_check_for_setup_error(self, req):
@ -265,30 +316,28 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_create_extend_delete_volume(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
self.driver.extend_volume(self.data.test_volume, 5)
self.driver.delete_volume(self.data.test_volume)
def test_create_delete_snapshot(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
self.driver.create_snapshot(self.data.test_snapshot)
self.assertEqual(self.data.test_snapshot['id'],
xms_data['volumes'][3]['name'])
xms_data['volumes'][2]['name'])
self.driver.delete_snapshot(self.data.test_snapshot)
self.driver.delete_volume(self.data.test_volume)
def test_failed_rename_snapshot(self, req):
req.side_effect = xms_failed_rename_snapshot_request
self.driver.create_snapshot(self.data.test_snapshot)
self.assertIn(27, xms_data['volumes'])
clean_xms_data()
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot,
self.data.test_snapshot)
self.assertEqual(0, len(xms_data['volumes']))
def test_volume_from_snapshot(self, req):
req.side_effect = xms_request
clean_xms_data()
xms_data['volumes'] = {}
self.driver.create_volume(self.data.test_volume)
self.driver.create_snapshot(self.data.test_snapshot)
@ -300,7 +349,6 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_clone_volume(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
self.driver.create_cloned_volume(self.data.test_clone,
self.data.test_volume)
@ -309,7 +357,6 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_duplicate_volume(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume, self.data.test_volume)
@ -317,7 +364,6 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_no_portals_configured(self, req):
req.side_effect = xms_request
clean_xms_data()
portals = xms_data['iscsi-portals'].copy()
xms_data['iscsi-portals'].clear()
lunmap = {'lun': 4}
@ -327,7 +373,6 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_initialize_terminate_connection(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
map_data = self.driver.initialize_connection(self.data.test_volume,
self.data.connector)
@ -337,7 +382,6 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_initialize_connection_bad_ig(self, req):
req.side_effect = xms_bad_request
clean_xms_data()
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.data.test_volume,
@ -346,17 +390,17 @@ class EMCXIODriverISCSITestCase(test.TestCase):
def test_get_stats(self, req):
req.side_effect = xms_request
clean_xms_data()
stats = self.driver.get_volume_stats(True)
self.assertEqual(stats['volume_backend_name'],
self.driver.backend_name)
def test_manage_unmanage(self, req):
req.side_effect = xms_request
clean_xms_data()
xms_data['volumes'] = {'unmanaged1': {'vol-name': 'unmanaged1',
'index': 'unmanaged1',
'vol-size': '3'}}
xms_data['volumes'] = {1: {'name': 'unmanaged1',
'index': 1,
'vol-size': '3',
},
}
ref_vol = {"source-name": "unmanaged1"}
invalid_ref = {"source-name": "invalid"}
self.assertRaises(exception.ManageExistingInvalidReference,
@ -371,26 +415,60 @@ class EMCXIODriverISCSITestCase(test.TestCase):
self.data.test_volume2)
self.driver.unmanage(self.data.test_volume)
@mock.patch('cinder.objects.snapshot.SnapshotList.get_all_for_cgsnapshot')
def test_cg_operations(self, get_all_for_cgsnapshot, req):
req.side_effect = xms_request
d = self.data
snapshot_obj = fake_snapshot.fake_snapshot_obj(d.context)
snapshot_obj.consistencygroup_id = d.group['id']
get_all_for_cgsnapshot.return_value = [snapshot_obj]
self.driver.create_consistencygroup(d.context, d.group)
self.assertEqual(1, len(xms_data['consistency-groups']))
self.driver.update_consistencygroup(d.context, d.group,
add_volumes=[d.test_volume,
d.test_volume2])
self.assertEqual(2, len(xms_data['consistency-group-volumes']))
self.driver.update_consistencygroup(d.context, d.group,
remove_volumes=[d.test_volume2])
self.assertEqual(1, len(xms_data['consistency-group-volumes']))
self.driver.db = mock.Mock()
(self.driver.db.
volume_get_all_by_group.return_value) = [mock.MagicMock()]
self.driver.create_cgsnapshot(d.context, d.cgsnapshot)
snaps_name = self.driver._get_cgsnap_name(d.cgsnapshot)
snaps = xms_data['volumes'][1]
snaps['index'] = 1
xms_data['snapshot-sets'] = {snaps_name: snaps, 1: snaps}
self.assertRaises(exception.InvalidInput,
self.driver.create_consistencygroup_from_src,
d.context, d.group, [])
self.driver.delete_cgsnapshot(d.context, d.cgsnapshot)
self.driver.delete_consistencygroup(d.context, d.group)
@mock.patch('cinder.volume.drivers.emc.xtremio.XtremIOClient.req')
class EMCXIODriverFibreChannelTestCase(test.TestCase):
def setUp(self):
super(EMCXIODriverFibreChannelTestCase, self).setUp()
clean_xms_data()
configuration = mock.Mock()
configuration.san_login = ''
configuration.san_password = ''
configuration.san_ip = ''
configuration.xtremio_cluster_name = ''
configuration.xtremio_provisioning_factor = 20.0
config = mock.Mock()
config.san_login = ''
config.san_password = ''
config.san_ip = ''
config.xtremio_cluster_name = ''
config.xtremio_provisioning_factor = 20.0
self.driver = xtremio.XtremIOFibreChannelDriver(
configuration=configuration)
configuration=config)
self.driver.client = xtremio.XtremIOClient4(config,
config.
xtremio_cluster_name)
self.data = CommonData()
def test_initialize_terminate_connection(self, req):
req.side_effect = xms_request
clean_xms_data()
self.driver.create_volume(self.data.test_volume)
map_data = self.driver.initialize_connection(self.data.test_volume,
self.data.connector)

View File

@ -22,7 +22,8 @@ supported XtremIO version 2.4 and up
1.0.3 - update logging level, add translation
1.0.4 - support for FC zones
1.0.5 - add support for XtremIO 4.0
1.0.6 - add support for iSCSI and CA validation
1.0.6 - add support for iSCSI multipath, CA validation, consistency groups,
R/O snapshots
"""
import json
@ -38,6 +39,7 @@ import six
from cinder import exception
from cinder.i18n import _, _LE, _LI, _LW
from cinder import objects
from cinder.volume import driver
from cinder.volume.drivers.san import san
from cinder.zonemanager import utils as fczm_utils
@ -163,6 +165,9 @@ class XtremIOClient(object):
"""
raise NotImplementedError()
def get_extra_capabilities(self):
return {}
class XtremIOClient3(XtremIOClient):
def __init__(self, configuration, cluster_id):
@ -217,6 +222,9 @@ class XtremIOClient4(XtremIOClient):
super(XtremIOClient4, self).__init__(configuration, cluster_id)
self._cluster_name = None
def get_extra_capabilities(self):
return {'consistencygroup_support': True}
def find_lunmap(self, ig_name, vol_name):
try:
return (self.req('lun-maps',
@ -269,6 +277,10 @@ class XtremIOClient4(XtremIOClient):
self.req(typ, 'DELETE', idx=int(idx))
raise
def add_vol_to_cg(self, vol_id, cg_id):
add_data = {'vol-id': vol_id, 'cg-id': cg_id}
self.req('consistency-group-volumes', 'POST', add_data, ver='v2')
class XtremIOVolumeDriver(san.SanDriver):
"""Executes commands relating to Volumes."""
@ -322,17 +334,28 @@ class XtremIOVolumeDriver(san.SanDriver):
data = {'vol-name': volume['id'],
'vol-size': str(volume['size']) + 'g'
}
self.client.req('volumes', 'POST', data)
if volume.get('consistencygroup_id'):
self.client.add_vol_to_cg(volume['id'],
volume['consistencygroup_id'])
def create_volume_from_snapshot(self, volume, snapshot):
"""Creates a volume from a snapshot."""
self.client.create_snapshot(snapshot.id, volume['id'])
if snapshot.get('consistencygroup_id'):
self.client.add_vol_to_cg(volume['id'],
snapshot['consistencygroup_id'])
def create_cloned_volume(self, volume, src_vref):
"""Creates a clone of the specified volume."""
self.client.create_snapshot(src_vref['id'], volume['id'])
if volume.get('consistencygroup_id'):
self.client.add_vol_to_cg(volume['id'],
volume['consistencygroup_id'])
def delete_volume(self, volume):
"""Deletes a volume."""
try:
@ -370,7 +393,9 @@ class XtremIOVolumeDriver(san.SanDriver):
'thick_provisioning_support': False,
'reserved_percentage':
self.configuration.reserved_percentage,
'QoS_support': False}
'QoS_support': False,
}
self._stats.update(self.client.get_extra_capabilities())
def get_volume_stats(self, refresh=False):
"""Get volume stats.
@ -435,7 +460,7 @@ class XtremIOVolumeDriver(san.SanDriver):
self.client.req('volumes', 'PUT', data, name=volume['id'])
except exception.NotFound:
msg = _("can't find the volume to extend")
raise (exception.VolumeDriverException(message=msg))
raise exception.VolumeDriverException(message=msg)
def check_for_export(self, context, volume_id):
"""Make sure volume is exported."""
@ -479,6 +504,117 @@ class XtremIOVolumeDriver(san.SanDriver):
def _get_ig(self, connector):
raise NotImplementedError()
def create_consistencygroup(self, context, group):
"""Creates a consistency group.
:param context: the context
:param group: the group object to be created
:returns: dict -- modelUpdate = {'status': 'available'}
:raises: VolumeBackendAPIException
"""
create_data = {'consistency-group-name': group['id']}
self.client.req('consistency-groups', 'POST', data=create_data,
ver='v2')
return {'status': 'available'}
def delete_consistencygroup(self, context, group):
"""Deletes a consistency group."""
self.client.req('consistency-groups', 'DELETE', name=group['id'],
ver='v2')
volumes = self.db.volume_get_all_by_group(context, group['id'])
for volume in volumes:
self.delete_volume(volume)
volume.status = 'deleted'
model_update = {'status': group['status']}
return model_update, volumes
def create_consistencygroup_from_src(self, context, group, volumes,
cgsnapshot=None, snapshots=None):
"""Creates a consistencygroup from source.
:param context: the context of the caller.
:param group: the dictionary of the consistency group to be created.
:param volumes: a list of volume dictionaries in the group.
:param cgsnapshot: the dictionary of the cgsnapshot as source.
:param snapshots: a list of snapshot dictionaries in the cgsnapshot.
:return model_update, volumes_model_update
"""
if cgsnapshot and snapshots:
for volume, snapshot in zip(volumes, snapshots):
self.create_volume_from_snapshot(volume, snapshot)
create_data = {'consistency-group-name': group['id'],
'vol-list': [v['id'] for v in volumes]}
self.client.req('consistency-groups', 'POST', data=create_data,
ver='v2')
else:
msg = _("create_consistencygroup_from_src only supports a"
" cgsnapshot source, other sources cannot be used.")
raise exception.InvalidInput(msg)
return None, None
def update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates a consistency group.
:param context: the context of the caller.
:param group: the dictionary of the consistency group to be updated.
:param add_volumes: a list of volume dictionaries to be added.
:param remove_volumes: a list of volume dictionaries to be removed.
:return model_update, add_volumes_update, remove_volumes_update
"""
add_volumes = add_volumes if add_volumes else []
remove_volumes = remove_volumes if remove_volumes else []
for vol in add_volumes:
add_data = {'vol-id': vol['id'], 'cg-id': group['id']}
self.client.req('consistency-group-volumes', 'POST', add_data,
ver='v2')
for vol in remove_volumes:
remove_data = {'vol-id': vol['id'], 'cg-id': group['id']}
self.client.req('consistency-group-volumes', 'DELETE', remove_data,
name=group['id'], ver='v2')
return None, None, None
def _get_cgsnap_name(self, cgsnapshot):
return '%(cg)s%(snap)s' % {'cg': cgsnapshot['consistencygroup_id']
.replace('-', ''),
'snap': cgsnapshot['id'].replace('-', '')}
def create_cgsnapshot(self, context, cgsnapshot):
"""Creates a cgsnapshot."""
data = {'consistency-group-id': cgsnapshot['consistencygroup_id'],
'snapshot-set-name': self._get_cgsnap_name(cgsnapshot)}
self.client.req('snapshots', 'POST', data, ver='v2')
snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
context, cgsnapshot['id'])
for snapshot in snapshots:
snapshot.status = 'available'
model_update = {'status': 'available'}
return model_update, snapshots
def delete_cgsnapshot(self, context, cgsnapshot):
"""Deletes a cgsnapshot."""
self.client.req('snapshot-sets', 'DELETE',
name=self._get_cgsnap_name(cgsnapshot), ver='v2')
snapshots = objects.SnapshotList().get_all_for_cgsnapshot(
context, cgsnapshot['id'])
for snapshot in snapshots:
snapshot.status = 'deleted'
model_update = {'status': cgsnapshot.status}
return model_update, snapshots
class XtremIOISCSIDriver(XtremIOVolumeDriver, driver.ISCSIDriver):
"""Executes commands relating to ISCSI volumes.