Merge "EMC VNX Direct Driver Consistency Group support"
This commit is contained in:
commit
52df6822f6
|
@ -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.")
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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']
|
||||
|
|
Loading…
Reference in New Issue