Added CG capability to volume group in CoprHD

CoprHD has been supporting consistency group starting from kilo release.
This code change makes sure that cg support is added for generic
volume groups for PIKE. Volume group maps to consistency group in
CoprHD when the consistency group snapshot flag is enabled for the
generic volume group.

Change-Id: I3ec946ca594e7c18a84f0b030607f0cc6f5864fb
Closes-Bug: #1682239
This commit is contained in:
Ravi Edpuganti 2017-04-25 14:22:49 +05:30 committed by Prashant Rakheja
parent 09cdfa0176
commit b248aad12a
6 changed files with 597 additions and 345 deletions

View File

@ -13,13 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
from mock import Mock
import mock
from cinder import context
from cinder import exception
from cinder.objects import fields
from cinder import test
from cinder.tests.unit import fake_constants
from cinder.tests.unit import fake_constants as fake
from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume.drivers.coprhd import fc as coprhd_fc
from cinder.volume.drivers.coprhd import iscsi as coprhd_iscsi
@ -185,60 +184,68 @@ scaleio_itl_list = {"itl": [{"hlu": -1,
"target": {}}]}
def get_test_volume_data(volume_type_id):
test_volume = {'name': 'test-vol1',
'size': 1,
'volume_name': 'test-vol1',
'id': '1',
'consistencygroup_id': None,
'provider_auth': None,
'project_id': 'project',
'display_name': 'test-vol1',
'display_description': 'test volume',
'volume_type_id': volume_type_id,
'provider_id': '1',
}
return test_volume
class test_volume_data(object):
name = 'test-vol1'
size = 1
volume_name = 'test-vol1'
id = fake.VOLUME_ID
group_id = None
provider_auth = None
project_id = fake.PROJECT_ID
display_name = 'test-vol1'
display_description = 'test volume',
volume_type_id = None
provider_id = fake.PROVIDER_ID
def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_source_test_volume_data(volume_type_id):
test_volume = {'name': 'source_test-vol1',
'size': 1,
'volume_name': 'source_test-vol1',
'id': '1234',
'consistencygroup_id': None,
'provider_auth': None,
'project_id': 'project',
'display_name': 'source_test-vol1',
'display_description': 'test volume',
'volume_type_id': volume_type_id}
return test_volume
class source_test_volume_data(object):
name = 'source_test-vol1'
size = 1
volume_name = 'source_test-vol1'
id = fake.VOLUME2_ID
group_id = None
provider_auth = None
project_id = fake.PROJECT_ID
display_name = 'source_test-vol1'
display_description = 'test volume'
volume_type_id = None
def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_clone_volume_data(volume_type_id):
clone_test_volume = {'name': 'clone-test-vol1',
'size': 1,
'volume_name': 'clone-test-vol1',
'id': '2',
'provider_auth': None,
'project_id': 'project',
'display_name': 'clone-test-vol1',
'display_description': 'clone test volume',
'volume_type_id': volume_type_id}
return clone_test_volume
class test_clone_volume_data(object):
name = 'clone-test-vol1'
size = 1
volume_name = 'clone-test-vol1'
id = fake.VOLUME3_ID
provider_auth = None
project_id = fake.PROJECT_ID
display_name = 'clone-test-vol1'
display_description = 'clone test volume'
volume_type_id = None
def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_test_snapshot_data(src_volume):
test_snapshot = {'name': 'snapshot1',
'display_name': 'snapshot1',
'size': 1,
'id': '1111',
'volume_name': 'test-vol1',
'volume_id': '1234',
'volume': src_volume,
'volume_size': 1,
'project_id': 'project'}
return test_snapshot
class test_snapshot_data(object):
name = 'snapshot1'
display_name = 'snapshot1'
size = 1
id = fake.SNAPSHOT_ID
volume_name = 'test-vol1'
volume_id = fake.VOLUME_ID
volume = None
volume_size = 1
project_id = fake.PROJECT_ID
status = fields.SnapshotStatus.AVAILABLE
def __init__(self, src_volume):
self.volume = src_volume
def get_connector_data():
@ -250,26 +257,41 @@ def get_connector_data():
return connector
def get_test_CG_data(volume_type_id):
test_CG = {'name': 'consistency_group_name',
'id': fake_constants.CONSISTENCY_GROUP_ID,
'volume_type_id': volume_type_id,
'status': fields.ConsistencyGroupStatus.AVAILABLE
}
return test_CG
class test_group_data(object):
name = 'group_name'
display_name = 'group_name'
id = fake.GROUP_ID
volume_type_ids = None
volume_types = None
group_type_id = None
status = fields.GroupStatus.AVAILABLE
def __init__(self, volume_types, group_type_id):
self.group_type_id = group_type_id
self.volume_types = volume_types
def get_test_CG_snap_data(volume_type_id):
test_CG_snapshot = {'name': 'cg_snap_name',
'id': fake_constants.SNAPSHOT_ID,
'consistencygroup_id':
fake_constants.CONSISTENCY_GROUP_ID,
'status': fields.ConsistencyGroupStatus.AVAILABLE,
'snapshots': [],
'consistencygroup': get_test_CG_data(volume_type_id),
'cgsnapshot_id': fake_constants.CGSNAPSHOT_ID,
}
return test_CG_snapshot
class test_group_type_data(object):
name = 'group_name'
display_name = 'group_name'
groupsnapshot_id = None
id = fake.GROUP_TYPE_ID
description = 'group'
class test_group_snap_data(object):
name = 'cg_snap_name'
display_name = 'cg_snap_name'
id = fake.GROUP_SNAPSHOT_ID
group_id = fake.GROUP_ID
status = fields.GroupStatus.AVAILABLE
snapshots = []
group = None
group_type_id = None
def __init__(self, volume_types, group_type_id):
self.group_type_id = group_type_id
self.group = test_group_data(volume_types, group_type_id)
class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
@ -300,22 +322,21 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
return "cg_uri"
def init_volume_api(self):
self.volume_api = Mock()
self.volume_api = mock.Mock()
self.volume_api.get.return_value = {
'name': 'source_test-vol1',
'size': 1,
'volume_name': 'source_test-vol1',
'id': fake_constants.VOLUME_ID,
'consistencygroup_id': fake_constants.CONSISTENCYGROUP_ID,
'id': fake.VOLUME_ID,
'group_id': fake.GROUP_ID,
'provider_auth': None,
'project_id': fake_constants.PROJECT_ID,
'project_id': fake.PROJECT_ID,
'display_name': 'source_test-vol1',
'display_description': 'test volume',
'volume_type_id': fake_constants.VOLUME_TYPE_ID,
}
'volume_type_id': fake.VOLUME_TYPE_ID}
def init_coprhd_api_components(self):
self.volume_obj = Mock()
self.volume_obj = mock.Mock()
self.volume_obj.create.return_value = "volume_created"
self.volume_obj.volume_query.return_value = "volume_uri"
self.volume_obj.get_storageAttributes.return_value = (
@ -342,12 +363,12 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
self.volume_obj.show.return_value = {"id": "vol_id"}
self.volume_obj.expand.return_value = "expanded"
self.tag_obj = Mock()
self.tag_obj = mock.Mock()
self.tag_obj.list_tags.return_value = [
"Openstack-vol", "Openstack-vol1"]
self.tag_obj.tag_resource.return_value = "Tagged"
self.exportgroup_obj = Mock()
self.exportgroup_obj = mock.Mock()
self.exportgroup_obj.exportgroup_list.return_value = (
export_group_list)
self.exportgroup_obj.exportgroup_show.return_value = (
@ -356,7 +377,7 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
self.exportgroup_obj.exportgroup_add_volumes.return_value = (
"volume-added")
self.host_obj = Mock()
self.host_obj = mock.Mock()
self.host_obj.list_by_tenant.return_value = []
self.host_obj.list_all.return_value = [{'id': "host1_id",
'name': "host1"}]
@ -365,11 +386,11 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
{'name': "12:34:56:78:90:54:32:11"},
{'name': "bfdf432500000004"}]
self.hostinitiator_obj = Mock()
self.varray_obj = Mock()
self.hostinitiator_obj = mock.Mock()
self.varray_obj = mock.Mock()
self.varray_obj.varray_show.return_value = varray_detail_data
self.snapshot_obj = Mock()
self.snapshot_obj = mock.Mock()
mocked_snap_obj = self.snapshot_obj.return_value
mocked_snap_obj.storageResource_query.return_value = (
"resourceUri")
@ -377,10 +398,10 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
"snapshot_created")
mocked_snap_obj.snapshot_query.return_value = "snapshot_uri"
self.consistencygroup_obj = Mock()
mocked_cg_object = self.consistencygroup_obj.return_value
mocked_cg_object.create.return_value = "CG-Created"
mocked_cg_object.consistencygroup_query.return_value = "CG-uri"
self.consistencygroup_obj = mock.Mock()
mocked_group_object = self.consistencygroup_obj.return_value
mocked_group_object.create.return_value = "CG-Created"
mocked_group_object.consistencygroup_query.return_value = "CG-uri"
class EMCCoprHDISCSIDriverTest(test.TestCase):
@ -391,7 +412,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
def create_coprhd_setup(self):
self.configuration = Mock()
self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDISCSIDriver"
@ -402,7 +423,10 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
self.configuration.coprhd_varray = "varray"
self.configuration.coprhd_emulate_snapshot = False
self.volume_type_id = self.create_coprhd_volume_type()
self.volume_type = self.create_coprhd_volume_type()
self.volume_type_id = self.volume_type.id
self.group_type = test_group_type_data()
self.group_type_id = self.group_type.id
self.mock_object(coprhd_iscsi.EMCCoprHDISCSIDriver,
'_get_common_driver',
@ -423,8 +447,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
"coprhd-volume-type",
{'CoprHD:VPOOL':
'vpool_coprhd'})
volume_id = vipr_volume_type['id']
return volume_id
return vipr_volume_type
def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon(
@ -437,7 +460,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self):
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
self.driver.delete_volume(volume)
@ -447,17 +470,17 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
self.assertEqual('unknown', vol_stats['free_capacity_gb'])
def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_volume_data(self.volume_type_id)
src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = test_clone_volume_data(self.volume_type_id)
self.driver.create_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, src_volume_data)
self.driver.delete_volume(src_volume_data)
self.driver.delete_volume(clone_volume_data)
def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(
get_source_test_volume_data(self.volume_type_id))
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(
source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data)
@ -466,11 +489,11 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
def test_create_volume_from_snapshot(self):
src_vol_data = get_source_test_volume_data(self.volume_type_id)
src_vol_data = source_test_volume_data(self.volume_type_id)
self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data)
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, snapshot_data)
@ -480,14 +503,14 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
self.driver.delete_volume(volume_data)
def test_extend_volume(self):
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data)
def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data()
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
res_initialize = self.driver.initialize_connection(
@ -498,54 +521,63 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
'target_iqn':
'50:00:09:73:00:18:95:19',
'target_discovered': False,
'volume_id': '1'}}
'volume_id': fake.VOLUME_ID}}
self.assertEqual(
expected_initialize, res_initialize, 'Unexpected return data')
self.driver.terminate_connection(volume_data, connector_data)
self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_empty_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, []))
self.driver.delete_group(ctx, group_data, []))
self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_update_delete_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True, True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
model_update, ret1, ret2 = (
self.driver.update_consistencygroup(ctx, cg_data, [volume], []))
self.driver.update_group(ctx, group_data, [volume], []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}],
self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update)
def test_create_delete_CG_snap(self):
cg_snap_data = get_test_CG_snap_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_group_snap(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_snap_data = test_group_snap_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = (
self.driver.delete_cgsnapshot(ctx, cg_snap_data, []))
self.driver.delete_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_model_update, 'Unexpected return data')
@ -558,7 +590,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def create_coprhd_setup(self):
self.configuration = Mock()
self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDFCDriver"
@ -569,7 +601,10 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.configuration.coprhd_varray = "varray"
self.configuration.coprhd_emulate_snapshot = False
self.volume_type_id = self.create_coprhd_volume_type()
self.volume_type = self.create_coprhd_volume_type()
self.volume_type_id = self.volume_type.id
self.group_type = test_group_type_data()
self.group_type_id = self.group_type.id
self.mock_object(coprhd_fc.EMCCoprHDFCDriver,
'_get_common_driver',
@ -589,8 +624,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
vipr_volume_type = volume_types.create(ctx,
"coprhd-volume-type",
{'CoprHD:VPOOL': 'vpool_vipr'})
volume_id = vipr_volume_type['id']
return volume_id
return vipr_volume_type
def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon(
@ -603,7 +637,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self):
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
self.driver.delete_volume(volume)
@ -614,8 +648,8 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_volume_data(self.volume_type_id)
src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = test_clone_volume_data(self.volume_type_id)
self.driver.create_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, src_volume_data)
self.driver.delete_volume(src_volume_data)
@ -623,9 +657,9 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(
get_source_test_volume_data(self.volume_type_id))
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(
source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data)
@ -633,11 +667,11 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.driver.delete_volume(volume_data)
def test_create_volume_from_snapshot(self):
src_vol_data = get_source_test_volume_data(self.volume_type_id)
src_vol_data = source_test_volume_data(self.volume_type_id)
self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data)
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, snapshot_data)
@ -646,22 +680,8 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.driver.delete_volume(src_vol_data)
self.driver.delete_volume(volume_data)
def test_create_volume_from_cg_snapshot(self):
ctx = context.get_admin_context()
volume_data = get_test_volume_data(self.volume_type_id)
cg_snap_data = get_test_CG_snap_data(self.volume_type_id)
self.driver.create_cgsnapshot(ctx, cg_snap_data, [])
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume_from_snapshot,
volume_data, cg_snap_data)
self.driver.delete_cgsnapshot(ctx, cg_snap_data, [])
self.driver.delete_volume(volume_data)
def test_extend_volume(self):
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data)
@ -669,7 +689,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data()
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
res_initiatlize = self.driver.initialize_connection(
@ -686,7 +706,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
'target_wwn': ['1234567890123456',
'1234567890123456'],
'target_discovered': False,
'volume_id': '1'}}
'volume_id': fake.VOLUME_ID}}
self.assertEqual(
expected_initialize, res_initiatlize, 'Unexpected return data')
@ -707,47 +727,56 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_empty_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, []))
self.driver.delete_group(ctx, group_data, []))
self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_update_delete_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
model_update, ret1, ret2 = (
self.driver.update_consistencygroup(ctx, cg_data, [volume], []))
self.driver.update_group(ctx, group_data, [volume], []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}],
self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update)
def test_create_delete_CG_snap(self):
cg_snap_data = get_test_CG_snap_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_group_snap(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_snap_data = test_group_snap_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = (
self.driver.delete_cgsnapshot(ctx, cg_snap_data, []))
self.driver.delete_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_model_update, 'Unexpected return data')
@ -760,7 +789,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def create_coprhd_setup(self):
self.configuration = Mock()
self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDFCDriver"
@ -779,7 +808,10 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
self.configuration.scaleio_server_certificate_path = (
"/etc/scaleio/certs")
self.volume_type_id = self.create_coprhd_volume_type()
self.volume_type = self.create_coprhd_volume_type()
self.volume_type_id = self.volume_type.id
self.group_type = test_group_type_data()
self.group_type_id = self.group_type.id
self.mock_object(coprhd_scaleio.EMCCoprHDScaleIODriver,
'_get_common_driver',
@ -802,8 +834,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
vipr_volume_type = volume_types.create(ctx,
"coprhd-volume-type",
{'CoprHD:VPOOL': 'vpool_vipr'})
volume_id = vipr_volume_type['id']
return volume_id
return vipr_volume_type
def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon(
@ -820,7 +851,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self):
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
self.driver.delete_volume(volume)
@ -831,8 +862,8 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_volume_data(self.volume_type_id)
src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = test_clone_volume_data(self.volume_type_id)
self.driver.create_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, src_volume_data)
self.driver.delete_volume(src_volume_data)
@ -840,9 +871,9 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(
get_source_test_volume_data(self.volume_type_id))
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(
source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data)
@ -850,11 +881,11 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
self.driver.delete_volume(volume_data)
def test_create_volume_from_snapshot(self):
src_vol_data = get_source_test_volume_data(self.volume_type_id)
src_vol_data = source_test_volume_data(self.volume_type_id)
self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data)
volume_data = test_volume_data(self.volume_type_id)
snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, snapshot_data)
@ -864,7 +895,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
self.driver.delete_volume(volume_data)
def test_extend_volume(self):
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data)
@ -872,16 +903,17 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data()
volume_data = get_test_volume_data(self.volume_type_id)
volume_data = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume_data)
res_initiatlize = self.driver.initialize_connection(
volume_data, connector_data)
exp_name = res_initiatlize['data']['scaleIO_volname']
expected_initialize = {'data': {'bandwidthLimit': None,
'hostIP': '10.0.0.2',
'iopsLimit': None,
'scaleIO_volname': 'test-vol1',
'scaleIO_volume_id': '1',
'scaleIO_volname': exp_name,
'scaleIO_volume_id': fake.PROVIDER_ID,
'serverIP': '10.10.10.11',
'serverPassword': 'scaleio_password',
'serverPort': 443,
@ -895,46 +927,55 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
volume_data, connector_data)
self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_empty_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, []))
self.driver.delete_group(ctx, group_data, []))
self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self):
cg_data = get_test_CG_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_update_delete_group(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True, True, True]
group_data = test_group_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data)
self.driver.create_group(ctx, group_data)
volume = get_test_volume_data(self.volume_type_id)
volume = test_volume_data(self.volume_type_id)
self.driver.create_volume(volume)
model_update, ret1, ret2 = (
self.driver.update_consistencygroup(ctx, cg_data, [volume], []))
self.driver.update_group(ctx, group_data, [volume], []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}],
self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update)
def test_create_delete_CG_snap(self):
cg_snap_data = get_test_CG_snap_data(self.volume_type_id)
@mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
def test_create_delete_group_snap(self, cg_ss_enabled):
cg_ss_enabled.side_effect = [True, True]
group_snap_data = test_group_snap_data([self.volume_type],
self.group_type_id)
ctx = context.get_admin_context()
model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE},
self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = (
self.driver.delete_cgsnapshot(ctx, cg_snap_data, []))
self.driver.delete_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_model_update, 'Unexpected return data')

View File

@ -45,9 +45,9 @@ from cinder.volume.drivers.coprhd.helpers import tag as coprhd_tag
from cinder.volume.drivers.coprhd.helpers import (
virtualarray as coprhd_varray)
from cinder.volume.drivers.coprhd.helpers import volume as coprhd_vol
from cinder.volume import utils as volume_utils
from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
MAX_RETRIES = 10
@ -88,6 +88,10 @@ CONF.register_opts(volume_opts, group=configuration.SHARED_CONF_GROUP)
URI_VPOOL_VARRAY_CAPACITY = '/block/vpools/{0}/varrays/{1}/capacity'
URI_BLOCK_EXPORTS_FOR_INITIATORS = '/block/exports?initiators={0}'
EXPORT_RETRY_COUNT = 5
MAX_DEFAULT_NAME_LENGTH = 128
MAX_SNAPSHOT_NAME_LENGTH = 63
MAX_CONSISTENCY_GROUP_NAME_LENGTH = 64
MAX_SIO_LEN = 31
def retry_wrapper(func):
@ -225,8 +229,9 @@ class EMCCoprHDDriverCommon(object):
def create_volume(self, vol, driver, truncate_name=False):
self.authenticate_user()
name = self._get_resource_name(vol, truncate_name)
size = int(vol['size']) * units.Gi
name = self._get_resource_name(vol, MAX_DEFAULT_NAME_LENGTH,
truncate_name)
size = int(vol.size) * units.Gi
vpool = self._get_vpool(vol)
self.vpool = vpool['CoprHD:VPOOL']
@ -234,14 +239,17 @@ class EMCCoprHDDriverCommon(object):
try:
coprhd_cgid = None
try:
cgid = vol['consistencygroup_id']
if cgid:
coprhd_cgid = self._get_coprhd_cgid(cgid)
if vol.group_id:
if volume_utils.is_group_a_cg_snapshot_type(vol.group):
coprhd_cgid = self._get_coprhd_cgid(vol.group_id)
except KeyError:
coprhd_cgid = None
except AttributeError:
coprhd_cgid = None
full_project_name = ("%s/%s" % (self.configuration.coprhd_tenant,
self.configuration.coprhd_project))
self.configuration.coprhd_project)
)
self.volume_obj.create(full_project_name, name, size,
self.configuration.coprhd_varray,
self.vpool,
@ -249,6 +257,7 @@ class EMCCoprHDDriverCommon(object):
sync=True,
# no longer specified in volume creation
consistencygroup=coprhd_cgid)
except coprhd_utils.CoprHdError as e:
coprhd_err_msg = (_("Volume %(name)s: create failed\n%(err)s") %
{'name': name, 'err': six.text_type(e.msg)})
@ -260,7 +269,9 @@ class EMCCoprHDDriverCommon(object):
@retry_wrapper
def create_consistencygroup(self, context, group, truncate_name=False):
self.authenticate_user()
name = self._get_resource_name(group, truncate_name)
name = self._get_resource_name(group,
MAX_CONSISTENCY_GROUP_NAME_LENGTH,
truncate_name)
try:
self.consistencygroup_obj.create(
@ -291,8 +302,8 @@ class EMCCoprHDDriverCommon(object):
def update_consistencygroup(self, group, add_volumes,
remove_volumes):
self.authenticate_user()
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE}
cg_uri = self._get_coprhd_cgid(group['id'])
model_update = {'status': fields.GroupStatus.AVAILABLE}
cg_uri = self._get_coprhd_cgid(group.id)
add_volnames = []
remove_volnames = []
@ -329,7 +340,9 @@ class EMCCoprHDDriverCommon(object):
def delete_consistencygroup(self, context, group, volumes,
truncate_name=False):
self.authenticate_user()
name = self._get_resource_name(group, truncate_name)
name = self._get_resource_name(group,
MAX_CONSISTENCY_GROUP_NAME_LENGTH,
truncate_name)
volumes_model_update = []
try:
@ -344,20 +357,20 @@ class EMCCoprHDDriverCommon(object):
sync=True,
force_delete=True)
update_item = {'id': vol['id'],
update_item = {'id': vol.id,
'status':
fields.ConsistencyGroupStatus.DELETED}
fields.GroupStatus.DELETED}
volumes_model_update.append(update_item)
except exception.VolumeBackendAPIException:
update_item = {'id': vol['id'],
update_item = {'id': vol.id,
'status': fields.ConsistencyGroupStatus.
ERROR_DELETING}
volumes_model_update.append(update_item)
LOG.exception("Failed to delete the volume %s of CG.",
vol['name'])
vol.name)
self.consistencygroup_obj.delete(
name,
@ -365,7 +378,7 @@ class EMCCoprHDDriverCommon(object):
self.configuration.coprhd_tenant)
model_update = {}
model_update['status'] = group['status']
model_update['status'] = group.status
return model_update, volumes_model_update
@ -384,9 +397,19 @@ class EMCCoprHDDriverCommon(object):
self.authenticate_user()
snapshots_model_update = []
cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name)
cg_id = cgsnapshot['consistencygroup_id']
cg_group = cgsnapshot.get('consistencygroup')
cgsnapshot_name = self._get_resource_name(cgsnapshot,
MAX_SNAPSHOT_NAME_LENGTH,
truncate_name)
cg_id = None
cg_group = None
try:
cg_id = cgsnapshot.group_id
cg_group = cgsnapshot.group
except AttributeError:
pass
cg_name = None
coprhd_cgid = None
@ -408,7 +431,7 @@ class EMCCoprHDDriverCommon(object):
True)
for snapshot in snapshots:
vol_id_of_snap = snapshot['volume_id']
vol_id_of_snap = snapshot.volume_id
# Finding the volume in CoprHD for this volume id
tagname = "OpenStack:id:" + vol_id_of_snap
@ -470,7 +493,7 @@ class EMCCoprHDDriverCommon(object):
snapshot['status'] = fields.SnapshotStatus.AVAILABLE
snapshots_model_update.append(
{'id': snapshot['id'], 'status':
{'id': snapshot.id, 'status':
fields.SnapshotStatus.AVAILABLE})
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE}
@ -493,19 +516,28 @@ class EMCCoprHDDriverCommon(object):
@retry_wrapper
def delete_cgsnapshot(self, cgsnapshot, snapshots, truncate_name=False):
self.authenticate_user()
cgsnapshot_id = cgsnapshot['id']
cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name)
cgsnapshot_id = cgsnapshot.id
cgsnapshot_name = self._get_resource_name(cgsnapshot,
MAX_SNAPSHOT_NAME_LENGTH,
truncate_name)
snapshots_model_update = []
cg_id = cgsnapshot['consistencygroup_id']
cg_group = cgsnapshot.get('consistencygroup')
cg_id = None
cg_group = None
try:
cg_id = cgsnapshot.group_id
cg_group = cgsnapshot.group
except AttributeError:
pass
coprhd_cgid = self._get_coprhd_cgid(cg_id)
cg_name = self._get_consistencygroup_name(cg_group)
model_update = {}
LOG.info('Delete cgsnapshot %(snap_name)s for consistency group: '
'%(group_name)s', {'snap_name': cgsnapshot['name'],
'%(group_name)s', {'snap_name': cgsnapshot.name,
'group_name': cg_name})
try:
@ -531,7 +563,7 @@ class EMCCoprHDDriverCommon(object):
for snapshot in snapshots:
snapshots_model_update.append(
{'id': snapshot['id'],
{'id': snapshot.id,
'status': fields.SnapshotStatus.DELETED})
return model_update, snapshots_model_update
@ -557,7 +589,9 @@ class EMCCoprHDDriverCommon(object):
exempt_tags = []
self.authenticate_user()
name = self._get_resource_name(vol, truncate_name)
name = self._get_resource_name(vol,
MAX_DEFAULT_NAME_LENGTH,
truncate_name)
full_project_name = ("%s/%s" % (
self.configuration.coprhd_tenant,
self.configuration.coprhd_project))
@ -613,11 +647,16 @@ class EMCCoprHDDriverCommon(object):
if ((not prop.startswith("status") and not
prop.startswith("obj_status") and
prop != "obj_volume") and value):
add_tags.append(
"%s:%s:%s" % (self.OPENSTACK_TAG, prop,
six.text_type(value)))
tag = ("%s:%s:%s" %
(self.OPENSTACK_TAG, prop,
six.text_type(value)))
if len(tag) > 128:
tag = tag[0:128]
add_tags.append(tag)
except TypeError:
LOG.error("Error tagging the resource property %s", prop)
LOG.error(
"Error tagging the resource property %s", prop)
except TypeError:
LOG.error("Error tagging the resource properties")
@ -638,17 +677,21 @@ class EMCCoprHDDriverCommon(object):
def create_cloned_volume(self, vol, src_vref, truncate_name=False):
"""Creates a clone of the specified volume."""
self.authenticate_user()
name = self._get_resource_name(vol, truncate_name)
name = self._get_resource_name(vol,
MAX_DEFAULT_NAME_LENGTH,
truncate_name)
srcname = self._get_coprhd_volume_name(src_vref)
try:
if src_vref['consistencygroup_id']:
if src_vref.group_id:
raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Clone can't be taken individually on a volume"
" that is part of a Consistency Group"))
except KeyError as e:
pass
except AttributeError:
pass
try:
(storageres_type,
storageres_typename) = self.volume_obj.get_storageAttributes(
@ -691,13 +734,21 @@ class EMCCoprHDDriverCommon(object):
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
try:
src_vol_size = src_vref['size']
except KeyError:
src_vol_size = src_vref['volume_size']
src_vol_size = 0
dest_vol_size = 0
if vol['size'] > src_vol_size:
size_in_bytes = coprhd_utils.to_bytes("%sG" % vol['size'])
try:
src_vol_size = src_vref.size
except AttributeError:
src_vol_size = src_vref.volume_size
try:
dest_vol_size = vol.size
except AttributeError:
dest_vol_size = vol.volume_size
if dest_vol_size > src_vol_size:
size_in_bytes = coprhd_utils.to_bytes("%sG" % dest_vol_size)
try:
self.volume_obj.expand(
("%s/%s" % (self.configuration.coprhd_tenant,
@ -733,7 +784,8 @@ class EMCCoprHDDriverCommon(object):
{'volume_name': volume_name,
'err': six.text_type(e.msg)})
log_err_msg = "Volume : %s expand failed" % volume_name
log_err_msg = ("Volume : %s expand failed" %
volume_name)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
@ -747,15 +799,20 @@ class EMCCoprHDDriverCommon(object):
self.create_cloned_volume(volume, snapshot, truncate_name)
return
if snapshot.get('cgsnapshot_id'):
raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Volume cannot be created individually from a snapshot "
"that is part of a Consistency Group"))
try:
if snapshot.group_snapshot_id:
raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Volume cannot be created individually from a snapshot "
"that is part of a Consistency Group"))
except AttributeError:
pass
src_snapshot_name = None
src_vol_ref = snapshot['volume']
new_volume_name = self._get_resource_name(volume, truncate_name)
src_vol_ref = snapshot.volume
new_volume_name = self._get_resource_name(volume,
MAX_DEFAULT_NAME_LENGTH,
truncate_name)
try:
coprhd_vol_info = self._get_coprhd_volume_name(
@ -786,12 +843,13 @@ class EMCCoprHDDriverCommon(object):
{'src_snapshot_name': src_snapshot_name,
'err': six.text_type(e.msg)})
log_err_msg = "Snapshot : %s clone failed" % src_snapshot_name
log_err_msg = ("Snapshot : %s clone failed" %
src_snapshot_name)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
if volume['size'] > snapshot['volume_size']:
size_in_bytes = coprhd_utils.to_bytes("%sG" % volume['size'])
if volume.size > snapshot.volume_size:
size_in_bytes = coprhd_utils.to_bytes("%sG" % volume.size)
try:
self.volume_obj.expand(
@ -805,7 +863,8 @@ class EMCCoprHDDriverCommon(object):
{'volume_name': new_volume_name,
'err': six.text_type(e.msg)})
log_err_msg = "Volume : %s expand failed" % new_volume_name
log_err_msg = ("Volume : %s expand failed" %
new_volume_name)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
@ -829,7 +888,7 @@ class EMCCoprHDDriverCommon(object):
"\n%(err)s") %
{'name': name, 'err': six.text_type(e.msg)})
log_err_msg = "Volume : %s delete failed" % name
log_err_msg = ("Volume : %s delete failed" % name)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
@ -837,10 +896,10 @@ class EMCCoprHDDriverCommon(object):
def create_snapshot(self, snapshot, truncate_name=False):
self.authenticate_user()
volume = snapshot['volume']
volume = snapshot.volume
try:
if volume['consistencygroup_id']:
if volume.group_id:
raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Snapshot can't be taken individually on a volume"
@ -855,8 +914,10 @@ class EMCCoprHDDriverCommon(object):
return
try:
snapshotname = self._get_resource_name(snapshot, truncate_name)
vol = snapshot['volume']
snapshotname = self._get_resource_name(snapshot,
MAX_SNAPSHOT_NAME_LENGTH,
truncate_name)
vol = snapshot.volume
volumename = self._get_coprhd_volume_name(vol)
projectname = self.configuration.coprhd_project
@ -894,7 +955,7 @@ class EMCCoprHDDriverCommon(object):
"\n%(err)s") % {'snapshotname': snapshotname,
'err': six.text_type(e.msg)})
log_err_msg = "Snapshot : %s create failed" % snapshotname
log_err_msg = ("Snapshot : %s create failed" % snapshotname)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
@ -902,10 +963,10 @@ class EMCCoprHDDriverCommon(object):
def delete_snapshot(self, snapshot):
self.authenticate_user()
vol = snapshot['volume']
vol = snapshot.volume
try:
if vol['consistencygroup_id']:
if vol.group_id:
raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Snapshot delete can't be done individually on a volume"
@ -949,7 +1010,7 @@ class EMCCoprHDDriverCommon(object):
coprhd_err_msg = (_("Snapshot %s : Delete Failed\n") %
snapshotname)
log_err_msg = "Snapshot : %s delete failed" % snapshotname
log_err_msg = ("Snapshot : %s delete failed" % snapshotname)
self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg)
@ -1170,7 +1231,7 @@ class EMCCoprHDDriverCommon(object):
(_("Consistency Group %s not found") % cgid))
def _get_consistencygroup_name(self, consisgrp):
return consisgrp['name']
return consisgrp.name
def _get_coprhd_snapshot_name(self, snapshot, resUri):
tagname = self.OPENSTACK_TAG + ":id:" + snapshot['id']
@ -1200,7 +1261,7 @@ class EMCCoprHDDriverCommon(object):
return rslt_snap['name']
def _get_coprhd_volume_name(self, vol, verbose=False):
tagname = self.OPENSTACK_TAG + ":id:" + vol['id']
tagname = self.OPENSTACK_TAG + ":id:" + vol.id
rslt = coprhd_utils.search_by_tag(
coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname),
self.configuration.coprhd_hostname,
@ -1210,7 +1271,7 @@ class EMCCoprHDDriverCommon(object):
# as "OpenStack:obj_id"
# as snapshots will be having the obj_id instead of just id.
if len(rslt) == 0:
tagname = self.OPENSTACK_TAG + ":obj_id:" + vol['id']
tagname = self.OPENSTACK_TAG + ":obj_id:" + vol.id
rslt = coprhd_utils.search_by_tag(
coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname),
self.configuration.coprhd_hostname,
@ -1228,21 +1289,36 @@ class EMCCoprHDDriverCommon(object):
coprhd_utils.CoprHdError.NOT_FOUND_ERR,
(_("Volume %s not found") % vol['display_name']))
def _get_resource_name(self, resource, truncate_name=False):
name = resource.get('display_name', None)
def _get_resource_name(self, resource,
max_name_cap=MAX_DEFAULT_NAME_LENGTH,
truncate_name=False):
# 36 refers to the length of UUID and +1 for '-'
permitted_name_length = max_name_cap - (36 + 1)
name = resource.display_name
if not name:
name = resource['name']
name = resource.name
if truncate_name and len(name) > 31:
'''
for scaleio, truncate_name will be true. We make sure the
total name is less than or equal to 31 characters.
_id_to_base64 will return a 24 character name'''
if truncate_name:
name = self._id_to_base64(resource.id)
return name
return name
elif len(name) > permitted_name_length:
'''
The maximum length of resource name in CoprHD is 128. Hence we use
only first 91 characters of the resource name'''
return name[0:permitted_name_length] + "-" + resource.id
else:
return name + "-" + resource.id
def _get_vpool(self, volume):
vpool = {}
ctxt = context.get_admin_context()
type_id = volume['volume_type_id']
type_id = volume.volume_type_id
if type_id is not None:
volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = volume_type.get('extra_specs')
@ -1363,7 +1439,8 @@ class EMCCoprHDDriverCommon(object):
self.authenticate_user()
try:
self.stats['consistencygroup_support'] = 'True'
self.stats['consistencygroup_support'] = True
self.stats['consistent_group_snapshot_enabled'] = True
vols = self.volume_obj.list_volumes(
self.configuration.coprhd_tenant +
"/" +

View File

@ -20,9 +20,13 @@ import re
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface
from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume import utils as volume_utils
from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__)
@ -89,30 +93,67 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
pass
def remove_export(self, context, volume):
"""Driver exntry point to remove an export for a volume."""
"""Driver entry point to remove an export for a volume."""
pass
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
return self.common.create_consistencygroup(context, group)
def create_group(self, context, group):
"""Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group)
def update_consistencygroup(self, context, group, add_volumes=None,
remove_volumes=None):
"""Updates volumes in consistency group."""
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_consistencygroup(self, context, group, volumes):
"""Deletes a consistency group."""
return self.common.delete_consistencygroup(context, group, volumes)
def update_group(self, context, group, add_volumes=None,
remove_volumes=None):
"""Updates volumes in group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a cgsnapshot."""
return self.common.create_cgsnapshot(cgsnapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a cgsnapshot."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots)
def create_group_from_src(self, ctxt, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
"""Creates a group from source."""
if volume_utils.is_group_a_cg_snapshot_type(group):
message = _("create group from source is not supported "
"for CoprHD if the group type supports "
"consistent group snapshot.")
raise exception.VolumeBackendAPIException(data=message)
else:
raise NotImplementedError()
def delete_group(self, context, group, volumes):
"""Deletes a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.delete_consistencygroup(context, group, volumes)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self.common.create_cgsnapshot(group_snapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self.common.delete_cgsnapshot(group_snapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def check_for_export(self, context, volume_id):
"""Make sure volume is exported."""
@ -123,14 +164,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
"""Initializes the connection and returns connection info."""
properties = {}
properties['volume_id'] = volume['id']
properties['volume_id'] = volume.id
properties['target_discovered'] = False
properties['target_wwn'] = []
init_ports = self._build_initport_list(connector)
itls = self.common.initialize_connection(volume,
'FC',
init_ports,
itls = self.common.initialize_connection(volume, 'FC', init_ports,
connector['host'])
target_wwns = None
@ -144,7 +183,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
properties['target_wwn'] = target_wwns
properties['initiator_target_map'] = initiator_target_map
auth = volume['provider_auth']
auth = None
try:
auth = volume.provider_auth
except AttributeError:
pass
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method
@ -162,9 +206,7 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
"""Driver entry point to detach a volume from an instance."""
init_ports = self._build_initport_list(connector)
itls = self.common.terminate_connection(volume,
'FC',
init_ports,
itls = self.common.terminate_connection(volume, 'FC', init_ports,
connector['host'])
volumes_count = self.common.get_exports_count_by_initiators(init_ports)

View File

@ -18,10 +18,12 @@
from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface
from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__)
@ -67,7 +69,7 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
self.common.expand_volume(volume, new_size)
def delete_volume(self, volume):
"""Deletes an volume."""
"""Deletes a volume."""
self.common.delete_volume(volume)
def create_snapshot(self, snapshot):
@ -90,27 +92,65 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
"""Driver entry point to remove an export for a volume."""
pass
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
return self.common.create_consistencygroup(context, group)
def create_group(self, context, group):
"""Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group)
def delete_consistencygroup(self, context, group, volumes):
"""Deletes a consistency group."""
return self.common.delete_consistencygroup(context, group, volumes)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates volumes in consistency group."""
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
def create_group_from_src(self, ctxt, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
"""Creates a group from source."""
if volume_utils.is_group_a_cg_snapshot_type(group):
message = _("create group from source is not supported "
"for CoprHD if the group type supports "
"consistent group snapshot.")
raise exception.VolumeBackendAPIException(data=message)
else:
raise NotImplementedError()
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a cgsnapshot."""
return self.common.create_cgsnapshot(cgsnapshot, snapshots)
def update_group(self, context, group, add_volumes=None,
remove_volumes=None):
"""Updates volumes in group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a cgsnapshot."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_group(self, context, group, volumes):
"""Deletes a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.delete_consistencygroup(context, group, volumes)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
LOG.debug("creating a group snapshot")
return self.common.create_cgsnapshot(group_snapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self.common.delete_cgsnapshot(group_snapshot, snapshots)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def check_for_export(self, context, volume_id):
"""Make sure volume is exported."""
@ -127,14 +167,20 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
connector['host'])
properties = {}
properties['target_discovered'] = False
properties['volume_id'] = volume['id']
properties['volume_id'] = volume.id
if itls:
properties['target_iqn'] = itls[0]['target']['port']
properties['target_portal'] = '%s:%s' % (
itls[0]['target']['ip_address'],
itls[0]['target']['tcp_port'])
properties['target_lun'] = itls[0]['hlu']
auth = volume['provider_auth']
auth = None
try:
auth = volume.provider_auth
except AttributeError:
pass
if auth:
(auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method

View File

@ -29,6 +29,7 @@ from cinder import interface
from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__)
@ -92,7 +93,7 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
"""Creates a Volume."""
self.common.create_volume(volume, self, True)
self.common.set_volume_tags(volume, ['_obj_volume_type'], True)
vol_size = self._update_volume_size(int(volume['size']))
vol_size = self._update_volume_size(int(volume.size))
return {'size': vol_size}
def _update_volume_size(self, vol_size):
@ -141,28 +142,68 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
"""Driver exntry point to remove an export for a volume."""
pass
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
return self.common.create_consistencygroup(context, group, True)
def create_group(self, context, group):
"""Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group, True)
def update_consistencygroup(self, context, group,
add_volumes=None, remove_volumes=None):
"""Updates volumes in consistency group."""
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_consistencygroup(self, context, group, volumes):
"""Deletes a consistency group."""
return self.common.delete_consistencygroup(context, group,
volumes, True)
def update_group(self, context, group, add_volumes=None,
remove_volumes=None):
"""Updates volumes in group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.update_consistencygroup(group, add_volumes,
remove_volumes)
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Creates a cgsnapshot."""
return self.common.create_cgsnapshot(cgsnapshot, snapshots, True)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Deletes a cgsnapshot."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots, True)
def create_group_from_src(self, ctxt, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
"""Creates a group from source."""
if volume_utils.is_group_a_cg_snapshot_type(group):
message = _("create group from source is not supported "
"for CoprHD if the group type supports "
"consistent group snapshot.")
raise exception.VolumeBackendAPIException(data=message)
else:
raise NotImplementedError()
def delete_group(self, context, group, volumes):
"""Deletes a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.delete_consistencygroup(context, group,
volumes, True)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Creates a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
LOG.debug("creating a group snapshot")
return self.common.create_cgsnapshot(group_snapshot, snapshots,
True)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a group snapshot."""
if volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
return self.common.delete_cgsnapshot(group_snapshot, snapshots,
True)
# If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def check_for_export(self, context, volume_id):
"""Make sure volume is exported."""
@ -171,11 +212,13 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info."""
volname = self.common._get_resource_name(volume, True)
volname = self.common._get_resource_name(volume,
coprhd_common.MAX_SIO_LEN,
True)
properties = {}
properties['scaleIO_volname'] = volname
properties['scaleIO_volume_id'] = volume['provider_id']
properties['scaleIO_volume_id'] = volume.provider_id
properties['hostIP'] = connector['ip']
properties[
'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host
@ -215,10 +258,10 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector."""
volname = volume['display_name']
volname = volume.display_name
properties = {}
properties['scaleIO_volname'] = volname
properties['scaleIO_volume_id'] = volume['provider_id']
properties['scaleIO_volume_id'] = volume.provider_id
properties['hostIP'] = connector['ip']
properties[
'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host

View File

@ -0,0 +1,3 @@
---
features:
- Add consistent group capability to generic volume groups in CoprHD driver.