Merge "EMC VNX Direct Driver Consistency Group support"

This commit is contained in:
Jenkins 2014-09-13 17:28:03 +00:00 committed by Gerrit Code Review
commit 52df6822f6
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']