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
'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'}]
}
'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'}]
}
'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}
'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'}
'provider_auth': None,
'project_id': 'project',
'display_name': 'failed_vol',
+ 'consistencygroup_id': None,
'display_description': 'test failed volume',
'volume_type_id': None}
test_snapshot = {
'id': '4444',
'volume_name': 'vol1',
'volume_size': 1,
+ 'consistencygroup_id': None,
+ 'cgsnapshot_id': None,
'project_id': 'project'}
test_failed_snapshot = {
'name': 'failed_snapshot',
'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 = {
'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'
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
"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."
'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
"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.")
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):
"""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)
\ No newline at end of file
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):
"""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)
\ No newline at end of file
import time
from oslo.config import cfg
+import six
from cinder import exception
from cinder.exception import EMCVnxCLICmdError
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)
@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',
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
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)
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
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,
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'])}
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
@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']
"""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']