EMC VNX Direct Driver Consistency Group support

Consistency Group support is newly introduced in Juno.

This commit is to add the changes in EMC VNX Direct Driver to
support CG.

Implements: blueprint emc-vnx-direct-driver-cg-support

Change-Id: I6c30ef2609ca8a559f5635129ce6a7c960a9f0a7
This commit is contained in:
Xing Yang 2014-09-09 18:18:21 -04:00
parent 5d2cc2f7f0
commit 3742e51312
4 changed files with 628 additions and 13 deletions

View File

@ -46,6 +46,35 @@ class EMCVNXCLIDriverTestData():
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None,
'consistencygroup_id': None,
'volume_admin_metadata': [{'key': 'readonly', 'value': 'True'}]
}
test_volume_clone_cg = {
'name': 'vol1',
'size': 1,
'volume_name': 'vol1',
'id': '1',
'provider_auth': None,
'project_id': 'project',
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None,
'consistencygroup_id': None,
'volume_admin_metadata': [{'key': 'readonly', 'value': 'True'}]
}
test_volume_cg = {
'name': 'vol1',
'size': 1,
'volume_name': 'vol1',
'id': '1',
'provider_auth': None,
'project_id': 'project',
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None,
'consistencygroup_id': 'cg_id',
'volume_admin_metadata': [{'key': 'readonly', 'value': 'True'}]
}
@ -59,7 +88,8 @@ class EMCVNXCLIDriverTestData():
'display_name': 'vol1',
'display_description': 'test volume',
'volume_type_id': None,
'volume_admin_metadata': [{'key': 'access_mode', 'value': 'rw'},
'consistencygroup_id': None,
'volume_admin_metadata': [{'key': 'attached_mode', 'value': 'rw'},
{'key': 'readonly', 'value': 'False'}]
}
@ -71,6 +101,19 @@ class EMCVNXCLIDriverTestData():
'provider_auth': None,
'project_id': 'project',
'display_name': 'vol2',
'consistencygroup_id': None,
'display_description': 'test volume',
'volume_type_id': None}
volume_in_cg = {
'name': 'vol2',
'size': 1,
'volume_name': 'vol2',
'id': '1',
'provider_auth': None,
'project_id': 'project',
'display_name': 'vol2',
'consistencygroup_id': None,
'display_description': 'test volume',
'volume_type_id': None}
@ -82,6 +125,7 @@ class EMCVNXCLIDriverTestData():
'provider_auth': None,
'project_id': 'project',
'display_name': 'thin_vol',
'consistencygroup_id': None,
'display_description': 'vol with type',
'volume_type_id': 'abc1-2320-9013-8813-8941-1374-8112-1231'}
@ -93,6 +137,7 @@ class EMCVNXCLIDriverTestData():
'provider_auth': None,
'project_id': 'project',
'display_name': 'failed_vol',
'consistencygroup_id': None,
'display_description': 'test failed volume',
'volume_type_id': None}
test_snapshot = {
@ -101,6 +146,8 @@ class EMCVNXCLIDriverTestData():
'id': '4444',
'volume_name': 'vol1',
'volume_size': 1,
'consistencygroup_id': None,
'cgsnapshot_id': None,
'project_id': 'project'}
test_failed_snapshot = {
'name': 'failed_snapshot',
@ -117,6 +164,18 @@ class EMCVNXCLIDriverTestData():
'provider_auth': None,
'project_id': 'project',
'display_name': 'clone1',
'consistencygroup_id': None,
'display_description': 'volume created from snapshot',
'volume_type_id': None}
test_clone_cg = {
'name': 'clone1',
'size': 1,
'id': '2',
'volume_name': 'vol1',
'provider_auth': None,
'project_id': 'project',
'display_name': 'clone1',
'consistencygroup_id': 'consistencygroup_id',
'display_description': 'volume created from snapshot',
'volume_type_id': None}
connector = {
@ -206,6 +265,26 @@ class EMCVNXCLIDriverTestData():
'volume_backend_name': 'array_backend_1',
'storage_protocol': 'iSCSI'}}
test_cg = {'id': 'consistencygroup_id',
'name': 'group_name',
'status': 'deleting'}
test_cgsnapshot = {
'consistencygroup_id': 'consistencygroup_id',
'id': 'cgsnapshot_id',
'status': 'available'}
test_member_cgsnapshot = {
'name': 'snapshot1',
'size': 1,
'id': 'cgsnapshot_id',
'volume_name': 'vol1',
'volume_size': 1,
'consistencygroup_id': 'consistencygroup_id',
'cgsnapshot_id': 'cgsnapshot_id',
'project_id': 'project'
}
test_lun_id = 1
test_existing_ref = {'id': test_lun_id}
test_pool_name = 'Pool_02_SASFLASH'
@ -324,6 +403,53 @@ class EMCVNXCLIDriverTestData():
return ('-np', 'storagepool', '-list', '-name',
storage_pool, '-fastcache')
def CREATE_CONSISTENCYGROUP_CMD(self, cg_name):
return ('-np', 'snap', '-group', '-create',
'-name', cg_name, '-allowSnapAutoDelete', 'no')
def DELETE_CONSISTENCYGROUP_CMD(self, cg_name):
return ('-np', 'snap', '-group', '-destroy',
'-id', cg_name)
def GET_CONSISTENCYGROUP_BY_NAME(self, cg_name):
return ('snap', '-group', '-list', '-id', cg_name)
def ADD_LUN_TO_CG_CMD(self, cg_name, lun_id):
return ('-np', 'snap', '-group',
'-addmember', '-id', cg_name, '-res', lun_id)
def CREATE_CG_SNAPSHOT(self, cg_name, snap_name):
return ('-np', 'snap', '-create', '-res', cg_name,
'-resType', 'CG', '-name', snap_name, '-allowReadWrite',
'yes', '-allowAutoDelete', 'no')
def DELETE_CG_SNAPSHOT(self, snap_name):
return ('-np', 'snap', '-destroy', '-id', snap_name, '-o')
def GET_CG_BY_NAME_CMD(self, cg_name):
return ('snap', '-group', '-list', '-id', cg_name)
def CONSISTENCY_GROUP_VOLUMES(self):
volumes = []
volumes.append(self.test_volume)
volumes.append(self.test_volume)
return volumes
def SNAPS_IN_SNAP_GROUP(self):
snaps = []
snaps.append(self.test_snapshot)
snaps.append(self.test_snapshot)
return snaps
def CG_PROPERTY(self, cg_name):
return """
Name: %(cg_name)s
Description:
Allow auto delete: No
Member LUN ID(s): 1, 3
State: Ready
""" % {'cg_name': cg_name}
POOL_PROPERTY = ("""\
Pool Name: unit_test_pool
Pool ID: 1
@ -851,7 +977,7 @@ class EMCVNXCLIDriverISCSITestCase(test.TestCase):
"volume backend name is not correct")
self.assertTrue(stats['location_info'] == "unit_test_pool|fakeSerial")
self.assertTrue(
stats['driver_version'] == "04.00.00",
stats['driver_version'] == "04.01.00",
"driver version is incorrect.")
@mock.patch("cinder.volume.drivers.emc.emc_vnx_cli."
@ -2028,6 +2154,185 @@ Time Remaining: 0 second(s)
'volume_admin_metadata': [{'key': 'readonly', 'value': 'True'}]}
self.assertEqual(self.driver.cli.get_lun_id(volume_02), 2)
def test_create_consistency_group(self):
cg_name = self.testData.test_cg['id']
commands = [self.testData.CREATE_CONSISTENCYGROUP_CMD(cg_name)]
results = [SUCCEED]
fake_cli = self.driverSetup(commands, results)
model_update = self.driver.create_consistencygroup(
None, self.testData.test_cg)
self.assertDictMatch({'status': 'available'}, model_update)
expect_cmd = [
mock.call(
*self.testData.CREATE_CONSISTENCYGROUP_CMD(
cg_name))]
fake_cli.assert_has_calls(expect_cmd)
def test_delete_consistency_group(self):
cg_name = self.testData.test_cg['id']
commands = [self.testData.DELETE_CONSISTENCYGROUP_CMD(cg_name),
self.testData.LUN_DELETE_CMD('vol1')]
results = [SUCCEED, SUCCEED]
fake_cli = self.driverSetup(commands, results)
self.driver.db = mock.MagicMock()
self.driver.db.volume_get_all_by_group.return_value =\
self.testData.CONSISTENCY_GROUP_VOLUMES()
self.driver.delete_consistencygroup(None,
self.testData.test_cg)
expect_cmd = [
mock.call(
*self.testData.DELETE_CONSISTENCYGROUP_CMD(
cg_name)),
mock.call(
*self.testData.LUN_DELETE_CMD('vol1')),
mock.call(
*self.testData.LUN_DELETE_CMD('vol1'))]
fake_cli.assert_has_calls(expect_cmd)
def test_create_cgsnapshot(self):
cgsnapshot = self.testData.test_cgsnapshot['id']
cg_name = self.testData.test_cgsnapshot['consistencygroup_id']
commands = [self.testData.CREATE_CG_SNAPSHOT(cg_name, cgsnapshot)]
results = [SUCCEED]
fake_cli = self.driverSetup(commands, results)
self.driver.db = mock.MagicMock()
self.driver.db.volume_get_all_by_group.return_value =\
self.testData.SNAPS_IN_SNAP_GROUP()
self.driver.create_cgsnapshot(None, self.testData.test_cgsnapshot)
expect_cmd = [
mock.call(
*self.testData.CREATE_CG_SNAPSHOT(
cg_name, cgsnapshot))]
fake_cli.assert_has_calls(expect_cmd)
def test_delete_cgsnapshot(self):
snap_name = self.testData.test_cgsnapshot['id']
commands = [self.testData.DELETE_CG_SNAPSHOT(snap_name)]
results = [SUCCEED]
fake_cli = self.driverSetup(commands, results)
self.driver.db = mock.MagicMock()
self.driver.db.snapshot_get_all_for_cgsnapshot.return_value =\
self.testData.SNAPS_IN_SNAP_GROUP()
self.driver.delete_cgsnapshot(None,
self.testData.test_cgsnapshot)
expect_cmd = [
mock.call(
*self.testData.DELETE_CG_SNAPSHOT(
snap_name))]
fake_cli.assert_has_calls(expect_cmd)
@mock.patch(
"eventlet.event.Event.wait",
mock.Mock(return_value=None))
def test_add_volume_to_cg(self):
commands = [self.testData.LUN_PROPERTY_ALL_CMD('vol1'),
self.testData.ADD_LUN_TO_CG_CMD('cg_id', 1),
self.testData.GET_CG_BY_NAME_CMD('cg_id')
]
results = [self.testData.LUN_PROPERTY('vol1', True),
SUCCEED,
self.testData.CG_PROPERTY('cg_id')]
fake_cli = self.driverSetup(commands, results)
self.driver.create_volume(self.testData.test_volume_cg)
expect_cmd = [
mock.call(*self.testData.LUN_CREATION_CMD(
'vol1', 1,
'unit_test_pool',
None, None)),
mock.call('lun', '-list', '-name', 'vol1',
'-state', '-status', '-opDetails',
'-userCap', '-owner', '-attachedSnapshot'),
mock.call(*self.testData.ADD_LUN_TO_CG_CMD(
'cg_id', 1))]
fake_cli.assert_has_calls(expect_cmd)
def test_create_cloned_volume_from_consistnecy_group(self):
cmd_smp = ('lun', '-list', '-name', 'vol1', '-attachedSnapshot')
output_smp = ("""LOGICAL UNIT NUMBER 1
Name: vol1
Attached Snapshot: N/A""", 0)
cmd_dest = self.testData.LUN_PROPERTY_ALL_CMD("vol1_dest")
output_dest = self.testData.LUN_PROPERTY("vol1_dest")
cmd_migrate = self.testData.MIGRATION_CMD(1, 1)
output_migrate = ("", 0)
cmd_migrate_verify = self.testData.MIGRATION_VERIFY_CMD(1)
output_migrate_verify = (r'The specified source LUN '
'is not currently migrating', 23)
cg_name = self.testData.test_cgsnapshot['consistencygroup_id']
commands = [cmd_smp, cmd_dest, cmd_migrate,
cmd_migrate_verify]
results = [output_smp, output_dest, output_migrate,
output_migrate_verify]
fake_cli = self.driverSetup(commands, results)
self.driver.create_cloned_volume(self.testData.test_volume_clone_cg,
self.testData.test_clone_cg)
tmp_cgsnapshot = 'tmp-cgsnapshot-' + self.testData.test_volume['id']
expect_cmd = [
mock.call(
*self.testData.CREATE_CG_SNAPSHOT(cg_name, tmp_cgsnapshot)),
mock.call(*self.testData.SNAP_MP_CREATE_CMD(name='vol1',
source='clone1')),
mock.call(
*self.testData.SNAP_ATTACH_CMD(
name='vol1', snapName=tmp_cgsnapshot)),
mock.call(*self.testData.LUN_CREATION_CMD(
'vol1_dest', 1, 'unit_test_pool', None, None)),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1_dest')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1_dest')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1_dest')),
mock.call(*self.testData.MIGRATION_CMD(1, 1),
retry_disable=True),
mock.call(*self.testData.MIGRATION_VERIFY_CMD(1)),
mock.call('lun', '-list', '-name', 'vol1', '-attachedSnapshot'),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol1')),
mock.call(*self.testData.DELETE_CG_SNAPSHOT(tmp_cgsnapshot))]
fake_cli.assert_has_calls(expect_cmd)
def test_create_volume_from_cgsnapshot(self):
cmd_smp = ('lun', '-list', '-name', 'vol2', '-attachedSnapshot')
output_smp = ("""LOGICAL UNIT NUMBER 1
Name: vol2
Attached Snapshot: N/A""", 0)
cmd_dest = self.testData.LUN_PROPERTY_ALL_CMD("vol2_dest")
output_dest = self.testData.LUN_PROPERTY("vol2_dest")
cmd_migrate = self.testData.MIGRATION_CMD(1, 1)
output_migrate = ("", 0)
cmd_migrate_verify = self.testData.MIGRATION_VERIFY_CMD(1)
output_migrate_verify = (r'The specified source LUN '
'is not currently migrating', 23)
commands = [cmd_smp, cmd_dest, cmd_migrate, cmd_migrate_verify]
results = [output_smp, output_dest, output_migrate,
output_migrate_verify]
fake_cli = self.driverSetup(commands, results)
self.driver.create_volume_from_snapshot(
self.testData.volume_in_cg, self.testData.test_member_cgsnapshot)
expect_cmd = [
mock.call(
*self.testData.SNAP_MP_CREATE_CMD(
name='vol2', source='vol1')),
mock.call(
*self.testData.SNAP_ATTACH_CMD(
name='vol2', snapName='cgsnapshot_id')),
mock.call(*self.testData.LUN_CREATION_CMD(
'vol2_dest', 1, 'unit_test_pool', None, None)),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol2_dest')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol2_dest')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol2')),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol2_dest')),
mock.call(*self.testData.MIGRATION_CMD(1, 1),
retry_disable=True),
mock.call(*self.testData.MIGRATION_VERIFY_CMD(1)),
mock.call('lun', '-list', '-name', 'vol2', '-attachedSnapshot'),
mock.call(*self.testData.LUN_PROPERTY_ALL_CMD('vol2'))]
fake_cli.assert_has_calls(expect_cmd)
def succeed_fake_command_execute(self, *command, **kwargv):
return SUCCEED
@ -2394,7 +2699,7 @@ class EMCVNXCLIDriverFCTestCase(test.TestCase):
"volume backend name is not correct")
self.assertTrue(stats['location_info'] == "unit_test_pool|fakeSerial")
self.assertTrue(
stats['driver_version'] == "04.00.00",
stats['driver_version'] == "04.01.00",
"driver version is incorrect.")

View File

@ -46,6 +46,7 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
FAST Cache Support), Storage-assisted Retype,
External Volume Management, Read-only Volume,
FC Auto Zoning
4.1.0 - Consistency group support
"""
def __init__(self, *args, **kwargs):
@ -217,3 +218,21 @@ class EMCCLIFCDriver(driver.FibreChannelDriver):
"""Return size of volume to be managed by manage_existing.
"""
return self.cli.manage_existing_get_size(volume, existing_ref)
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
return self.cli.create_consistencygroup(context, group)
def delete_consistencygroup(self, context, group):
"""Deletes a consistency group."""
return self.cli.delete_consistencygroup(
self, context, group)
def create_cgsnapshot(self, context, cgsnapshot):
"""Creates a cgsnapshot."""
return self.cli.create_cgsnapshot(
self, context, cgsnapshot)
def delete_cgsnapshot(self, context, cgsnapshot):
"""Deletes a cgsnapshot."""
return self.cli.delete_cgsnapshot(self, context, cgsnapshot)

View File

@ -43,6 +43,7 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
FAST Cache Support), Storage-assisted Retype,
External Volume Management, Read-only Volume,
FC Auto Zoning
4.1.0 - Consistency group support
"""
def __init__(self, *args, **kwargs):
@ -174,3 +175,21 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver):
"""Return size of volume to be managed by manage_existing.
"""
return self.cli.manage_existing_get_size(volume, existing_ref)
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
return self.cli.create_consistencygroup(context, group)
def delete_consistencygroup(self, context, group):
"""Deletes a consistency group."""
return self.cli.delete_consistencygroup(
self, context, group)
def create_cgsnapshot(self, context, cgsnapshot):
"""Creates a cgsnapshot."""
return self.cli.create_cgsnapshot(
self, context, cgsnapshot)
def delete_cgsnapshot(self, context, cgsnapshot):
"""Deletes a cgsnapshot."""
return self.cli.delete_cgsnapshot(self, context, cgsnapshot)

View File

@ -22,6 +22,7 @@ import re
import time
from oslo.config import cfg
import six
from cinder import exception
from cinder.exception import EMCVnxCLICmdError
@ -187,6 +188,9 @@ class CommandLineHelper(object):
POOL_ALL = [POOL_TOTAL_CAPACITY, POOL_FREE_CAPACITY]
CLI_RESP_PATTERN_CG_NOT_FOUND = 'Cannot find'
CLI_RESP_PATTERN_SNAP_NOT_FOUND = 'The specified snapshot does not exist'
def __init__(self, configuration):
configuration.append_config_values(san.san_opts)
@ -281,7 +285,8 @@ class CommandLineHelper(object):
@log_enter_exit
def create_lun_with_advance_feature(self, pool, name, size,
provisioning, tiering):
provisioning, tiering,
consistencygroup_id=None):
command_create_lun = ['lun', '-create',
'-capacity', size,
'-sq', 'gb',
@ -305,7 +310,19 @@ class CommandLineHelper(object):
except EMCVnxCLICmdError as ex:
with excutils.save_and_reraise_exception():
self.delete_lun(name)
LOG.error(_("Failed to enable compression on lun: %s") % ex)
LOG.error(_("Error on enable compression on lun %s.")
% six.text_type(ex))
# handle consistency group
try:
if consistencygroup_id:
self.add_lun_to_consistency_group(
consistencygroup_id, data['lun_id'])
except EMCVnxCLICmdError as ex:
with excutils.save_and_reraise_exception():
self.delete_lun(name)
LOG.error(_("Error on adding lun to consistency"
" group. %s") % six.text_type(ex))
return data
@log_enter_exit
@ -432,6 +449,143 @@ class CommandLineHelper(object):
if rc != 0:
raise EMCVnxCLICmdError(command_modify_lun, rc, out)
@log_enter_exit
def create_consistencygroup(self, context, group):
"""create the consistency group."""
cg_name = group['id']
command_create_cg = ('-np', 'snap', '-group',
'-create',
'-name', cg_name,
'-allowSnapAutoDelete', 'no')
out, rc = self.command_execute(*command_create_cg)
if rc != 0:
# Ignore the error if consistency group already exists
if (rc == 33 and
out.find("(0x716d8021)") >= 0):
LOG.warn(_('Consistency group %(name)s already '
'exists. Message: %(msg)s') %
{'name': cg_name, 'msg': out})
else:
raise EMCVnxCLICmdError(command_create_cg, rc, out)
@log_enter_exit
def get_consistency_group_by_name(self, cg_name):
cmd = ('snap', '-group', '-list', '-id', cg_name)
data = {
'Name': None,
'Luns': None,
'State': None
}
out, rc = self.command_execute(*cmd)
if rc == 0:
cg_pat = r"Name:(.*)\n"\
r"Description:(.*)\n"\
r"Allow auto delete:(.*)\n"\
r"Member LUN ID\(s\):(.*)\n"\
r"State:(.*)\n"
for m in re.finditer(cg_pat, out):
data['Name'] = m.groups()[0].strip()
data['State'] = m.groups()[4].strip()
luns_of_cg = m.groups()[3].split(',')
if luns_of_cg:
data['Luns'] = [lun.strip() for lun in luns_of_cg]
LOG.debug("Found consistent group %s." % data['Name'])
return data
@log_enter_exit
def add_lun_to_consistency_group(self, cg_name, lun_id):
add_lun_to_cg_cmd = ('-np', 'snap', '-group',
'-addmember', '-id',
cg_name, '-res', lun_id)
out, rc = self.command_execute(*add_lun_to_cg_cmd)
if rc != 0:
msg = (_("Can not add the lun %(lun)s to consistency "
"group %(cg_name)s.") % {'lun': lun_id,
'cg_name': cg_name})
LOG.error(msg)
raise EMCVnxCLICmdError(add_lun_to_cg_cmd, rc, out)
def add_lun_to_consistency_success():
data = self.get_consistency_group_by_name(cg_name)
if str(lun_id) in data['Luns']:
LOG.debug(("Add lun %(lun)s to consistency "
"group %(cg_name)s successfully.") %
{'lun': lun_id, 'cg_name': cg_name})
return True
else:
LOG.debug(("Adding lun %(lun)s to consistency "
"group %(cg_name)s.") %
{'lun': lun_id, 'cg_name': cg_name})
return False
self._wait_for_a_condition(add_lun_to_consistency_success,
interval=INTERVAL_30_SEC)
@log_enter_exit
def delete_consistencygroup(self, cg_name):
delete_cg_cmd = ('-np', 'snap', '-group',
'-destroy', '-id', cg_name)
out, rc = self.command_execute(*delete_cg_cmd)
if rc != 0:
# Ignore the error if CG doesn't exist
if rc == 13 and out.find(self.CLI_RESP_PATTERN_CG_NOT_FOUND) >= 0:
LOG.warn(_("CG %(cg_name)s does not exist. "
"Message: %(msg)s") %
{'cg_name': cg_name, 'msg': out})
elif rc == 1 and out.find("0x712d8801") >= 0:
LOG.warn(_("CG %(cg_name)s is deleting. "
"Message: %(msg)s") %
{'cg_name': cg_name, 'msg': out})
else:
raise EMCVnxCLICmdError(delete_cg_cmd, rc, out)
else:
LOG.info(_('Consistency group %s was deleted '
'successfully.') % cg_name)
@log_enter_exit
def create_cgsnapshot(self, cgsnapshot):
"""Create a cgsnapshot (snap group)."""
cg_name = cgsnapshot['consistencygroup_id']
snap_name = cgsnapshot['id']
create_cg_snap_cmd = ('-np', 'snap', '-create',
'-res', cg_name,
'-resType', 'CG',
'-name', snap_name,
'-allowReadWrite', 'yes',
'-allowAutoDelete', 'no')
out, rc = self.command_execute(*create_cg_snap_cmd)
if rc != 0:
# Ignore the error if cgsnapshot already exists
if (rc == 5 and
out.find("(0x716d8005)") >= 0):
LOG.warn(_('Cgsnapshot name %(name)s already '
'exists. Message: %(msg)s') %
{'name': snap_name, 'msg': out})
else:
raise EMCVnxCLICmdError(create_cg_snap_cmd, rc, out)
@log_enter_exit
def delete_cgsnapshot(self, cgsnapshot):
"""Delete a cgsnapshot (snap group)."""
snap_name = cgsnapshot['id']
delete_cg_snap_cmd = ('-np', 'snap', '-destroy',
'-id', snap_name, '-o')
out, rc = self.command_execute(*delete_cg_snap_cmd)
if rc != 0:
# Ignore the error if cgsnapshot does not exist.
if (rc == 5 and
out.find(self.CLI_RESP_PATTERN_SNAP_NOT_FOUND) >= 0):
LOG.warn(_('Snapshot %(name)s for consistency group '
'does not exist. Message: %(msg)s') %
{'name': snap_name, 'msg': out})
else:
raise EMCVnxCLICmdError(delete_cg_snap_cmd, rc, out)
@log_enter_exit
def create_snapshot(self, volume_name, name):
data = self.get_lun_by_name(volume_name)
@ -445,15 +599,15 @@ class CommandLineHelper(object):
out, rc = self.command_execute(*command_create_snapshot)
if rc != 0:
# Ignore the error that due to retry
if rc == 5 and \
out.find("(0x716d8005)") >= 0:
if (rc == 5 and
out.find("(0x716d8005)") >= 0):
LOG.warn(_('Snapshot %(name)s already exists. '
'Message: %(msg)s') %
{'name': name, 'msg': out})
else:
raise EMCVnxCLICmdError(command_create_snapshot, rc, out)
else:
msg = _('Failed to get LUN ID for volume %s') % volume_name
msg = _('Failed to get LUN ID for volume %s.') % volume_name
raise exception.VolumeBackendAPIException(data=msg)
@log_enter_exit
@ -1265,7 +1419,7 @@ class CommandLineHelper(object):
class EMCVnxCliBase(object):
"""This class defines the functions to use the native CLI functionality."""
VERSION = '04.00.00'
VERSION = '04.01.00'
stats = {'driver_version': VERSION,
'free_capacity_gb': 'unknown',
'reserved_percentage': 0,
@ -1346,7 +1500,7 @@ class EMCVnxCliBase(object):
data = self._client.create_lun_with_advance_feature(
pool, volumename, volumesize,
provisioning, tiering)
provisioning, tiering, volume['consistencygroup_id'])
pl_dict = {'system': self.get_array_serial(),
'type': 'lun',
'id': str(data['lun_id'])}
@ -1676,6 +1830,10 @@ class EMCVnxCliBase(object):
self.stats['fast_cache_enabled'] = 'True'
else:
self.stats['fast_cache_enabled'] = 'False'
if '-VNXSnapshots' in self.enablers:
self.stats['consistencygroup_support'] = 'True'
else:
self.stats['consistencygroup_support'] = 'False'
return self.stats
@ -1722,7 +1880,10 @@ class EMCVnxCliBase(object):
@log_enter_exit
def create_volume_from_snapshot(self, volume, snapshot):
"""Creates a volume from a snapshot."""
snapshot_name = snapshot['name']
if snapshot['cgsnapshot_id']:
snapshot_name = snapshot['cgsnapshot_id']
else:
snapshot_name = snapshot['name']
source_volume_name = snapshot['volume_name']
volume_name = volume['name']
volume_size = snapshot['volume_size']
@ -1772,21 +1933,132 @@ class EMCVnxCliBase(object):
"""Creates a clone of the specified volume."""
source_volume_name = src_vref['name']
volume_size = src_vref['size']
consistencygroup_id = src_vref['consistencygroup_id']
snapshot_name = 'tmp-snap-%s' % volume['id']
tmp_cgsnapshot_name = None
if consistencygroup_id:
tmp_cgsnapshot_name = 'tmp-cgsnapshot-%s' % volume['id']
snapshot = {
'name': snapshot_name,
'volume_name': source_volume_name,
'volume_size': volume_size,
'cgsnapshot_id': tmp_cgsnapshot_name,
'consistencygroup_id': consistencygroup_id,
'id': tmp_cgsnapshot_name
}
# Create temp Snapshot
self.create_snapshot(snapshot)
if consistencygroup_id:
self._client.create_cgsnapshot(snapshot)
else:
self.create_snapshot(snapshot)
# Create volume
model_update = self.create_volume_from_snapshot(volume, snapshot)
# Delete temp Snapshot
self.delete_snapshot(snapshot)
if consistencygroup_id:
self._client.delete_cgsnapshot(snapshot)
else:
self.delete_snapshot(snapshot)
return model_update
@log_enter_exit
def create_consistencygroup(self, context, group):
"""Create a consistency group."""
LOG.info(_('Start to create consistency group: %(group_name)s '
'id: %(id)s') %
{'group_name': group['name'], 'id': group['id']})
model_update = {'status': 'available'}
try:
self._client.create_consistencygroup(context, group)
except Exception:
with excutils.save_and_reraise_exception():
msg = (_('Create consistency group %s failed.')
% group['id'])
LOG.error(msg)
return model_update
@log_enter_exit
def delete_consistencygroup(self, driver, context, group):
"""Delete a consistency group."""
cg_name = group['id']
volumes = driver.db.volume_get_all_by_group(context, group['id'])
model_update = {}
model_update['status'] = group['status']
LOG.info(_('Start to delete consistency group: %(cg_name)s')
% {'cg_name': cg_name})
try:
self._client.delete_consistencygroup(cg_name)
except Exception:
with excutils.save_and_reraise_exception():
msg = (_('Delete consistency group %s failed.')
% cg_name)
LOG.error(msg)
for volume_ref in volumes:
try:
self._client.delete_lun(volume_ref['name'])
volume_ref['status'] = 'deleted'
except Exception:
volume_ref['status'] = 'error_deleting'
model_update['status'] = 'error_deleting'
return model_update, volumes
@log_enter_exit
def create_cgsnapshot(self, driver, context, cgsnapshot):
"""Create a cgsnapshot (snap group)."""
cgsnapshot_id = cgsnapshot['id']
snapshots = driver.db.snapshot_get_all_for_cgsnapshot(
context, cgsnapshot_id)
model_update = {}
LOG.info(_('Start to create cgsnapshot for consistency group'
': %(group_name)s') %
{'group_name': cgsnapshot['consistencygroup_id']})
try:
self._client.create_cgsnapshot(cgsnapshot)
for snapshot in snapshots:
snapshot['status'] = 'available'
except Exception:
with excutils.save_and_reraise_exception():
msg = (_('Create cg snapshot %s failed.')
% cgsnapshot_id)
LOG.error(msg)
model_update['status'] = 'available'
return model_update, snapshots
@log_enter_exit
def delete_cgsnapshot(self, driver, context, cgsnapshot):
"""delete a cgsnapshot (snap group)."""
cgsnapshot_id = cgsnapshot['id']
snapshots = driver.db.snapshot_get_all_for_cgsnapshot(
context, cgsnapshot_id)
model_update = {}
model_update['status'] = cgsnapshot['status']
LOG.info(_('Delete cgsnapshot %(snap_name)s for consistency group: '
'%(group_name)s') % {'snap_name': cgsnapshot['id'],
'group_name': cgsnapshot['consistencygroup_id']})
try:
self._client.delete_cgsnapshot(cgsnapshot)
for snapshot in snapshots:
snapshot['status'] = 'deleted'
except Exception:
with excutils.save_and_reraise_exception():
msg = (_('Delete cgsnapshot %s failed.')
% cgsnapshot_id)
LOG.error(msg)
return model_update, snapshots
def get_lun_id_by_name(self, volume_name):
data = self._client.get_lun_by_name(volume_name)
return data['lun_id']