Merge "Added CG capability to volume group in CoprHD"

This commit is contained in:
Jenkins 2017-07-21 03:20:55 +00:00 committed by Gerrit Code Review
commit dd8e524e94
6 changed files with 597 additions and 345 deletions

View File

@ -13,13 +13,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from mock import Mock import mock
from cinder import context from cinder import context
from cinder import exception
from cinder.objects import fields from cinder.objects import fields
from cinder import test 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 common as coprhd_common
from cinder.volume.drivers.coprhd import fc as coprhd_fc from cinder.volume.drivers.coprhd import fc as coprhd_fc
from cinder.volume.drivers.coprhd import iscsi as coprhd_iscsi from cinder.volume.drivers.coprhd import iscsi as coprhd_iscsi
@ -185,60 +184,68 @@ scaleio_itl_list = {"itl": [{"hlu": -1,
"target": {}}]} "target": {}}]}
def get_test_volume_data(volume_type_id): class test_volume_data(object):
test_volume = {'name': 'test-vol1', name = 'test-vol1'
'size': 1, size = 1
'volume_name': 'test-vol1', volume_name = 'test-vol1'
'id': '1', id = fake.VOLUME_ID
'consistencygroup_id': None, group_id = None
'provider_auth': None, provider_auth = None
'project_id': 'project', project_id = fake.PROJECT_ID
'display_name': 'test-vol1', display_name = 'test-vol1'
'display_description': 'test volume', display_description = 'test volume',
'volume_type_id': volume_type_id, volume_type_id = None
'provider_id': '1', provider_id = fake.PROVIDER_ID
}
return test_volume def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_source_test_volume_data(volume_type_id): class source_test_volume_data(object):
test_volume = {'name': 'source_test-vol1', name = 'source_test-vol1'
'size': 1, size = 1
'volume_name': 'source_test-vol1', volume_name = 'source_test-vol1'
'id': '1234', id = fake.VOLUME2_ID
'consistencygroup_id': None, group_id = None
'provider_auth': None, provider_auth = None
'project_id': 'project', project_id = fake.PROJECT_ID
'display_name': 'source_test-vol1', display_name = 'source_test-vol1'
'display_description': 'test volume', display_description = 'test volume'
'volume_type_id': volume_type_id} volume_type_id = None
return test_volume
def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_clone_volume_data(volume_type_id): class test_clone_volume_data(object):
clone_test_volume = {'name': 'clone-test-vol1', name = 'clone-test-vol1'
'size': 1, size = 1
'volume_name': 'clone-test-vol1', volume_name = 'clone-test-vol1'
'id': '2', id = fake.VOLUME3_ID
'provider_auth': None, provider_auth = None
'project_id': 'project', project_id = fake.PROJECT_ID
'display_name': 'clone-test-vol1', display_name = 'clone-test-vol1'
'display_description': 'clone test volume', display_description = 'clone test volume'
'volume_type_id': volume_type_id} volume_type_id = None
return clone_test_volume
def __init__(self, volume_type_id):
self.volume_type_id = volume_type_id
def get_test_snapshot_data(src_volume): class test_snapshot_data(object):
test_snapshot = {'name': 'snapshot1', name = 'snapshot1'
'display_name': 'snapshot1', display_name = 'snapshot1'
'size': 1, size = 1
'id': '1111', id = fake.SNAPSHOT_ID
'volume_name': 'test-vol1', volume_name = 'test-vol1'
'volume_id': '1234', volume_id = fake.VOLUME_ID
'volume': src_volume, volume = None
'volume_size': 1, volume_size = 1
'project_id': 'project'} project_id = fake.PROJECT_ID
return test_snapshot status = fields.SnapshotStatus.AVAILABLE
def __init__(self, src_volume):
self.volume = src_volume
def get_connector_data(): def get_connector_data():
@ -250,26 +257,41 @@ def get_connector_data():
return connector return connector
def get_test_CG_data(volume_type_id): class test_group_data(object):
test_CG = {'name': 'consistency_group_name', name = 'group_name'
'id': fake_constants.CONSISTENCY_GROUP_ID, display_name = 'group_name'
'volume_type_id': volume_type_id, id = fake.GROUP_ID
'status': fields.ConsistencyGroupStatus.AVAILABLE volume_type_ids = None
} volume_types = None
return test_CG 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): class test_group_type_data(object):
test_CG_snapshot = {'name': 'cg_snap_name', name = 'group_name'
'id': fake_constants.SNAPSHOT_ID, display_name = 'group_name'
'consistencygroup_id': groupsnapshot_id = None
fake_constants.CONSISTENCY_GROUP_ID, id = fake.GROUP_TYPE_ID
'status': fields.ConsistencyGroupStatus.AVAILABLE, description = 'group'
'snapshots': [],
'consistencygroup': get_test_CG_data(volume_type_id),
'cgsnapshot_id': fake_constants.CGSNAPSHOT_ID, class test_group_snap_data(object):
} name = 'cg_snap_name'
return test_CG_snapshot 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): class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
@ -300,22 +322,21 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
return "cg_uri" return "cg_uri"
def init_volume_api(self): def init_volume_api(self):
self.volume_api = Mock() self.volume_api = mock.Mock()
self.volume_api.get.return_value = { self.volume_api.get.return_value = {
'name': 'source_test-vol1', 'name': 'source_test-vol1',
'size': 1, 'size': 1,
'volume_name': 'source_test-vol1', 'volume_name': 'source_test-vol1',
'id': fake_constants.VOLUME_ID, 'id': fake.VOLUME_ID,
'consistencygroup_id': fake_constants.CONSISTENCYGROUP_ID, 'group_id': fake.GROUP_ID,
'provider_auth': None, 'provider_auth': None,
'project_id': fake_constants.PROJECT_ID, 'project_id': fake.PROJECT_ID,
'display_name': 'source_test-vol1', 'display_name': 'source_test-vol1',
'display_description': 'test volume', '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): 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.create.return_value = "volume_created"
self.volume_obj.volume_query.return_value = "volume_uri" self.volume_obj.volume_query.return_value = "volume_uri"
self.volume_obj.get_storageAttributes.return_value = ( 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.show.return_value = {"id": "vol_id"}
self.volume_obj.expand.return_value = "expanded" self.volume_obj.expand.return_value = "expanded"
self.tag_obj = Mock() self.tag_obj = mock.Mock()
self.tag_obj.list_tags.return_value = [ self.tag_obj.list_tags.return_value = [
"Openstack-vol", "Openstack-vol1"] "Openstack-vol", "Openstack-vol1"]
self.tag_obj.tag_resource.return_value = "Tagged" self.tag_obj.tag_resource.return_value = "Tagged"
self.exportgroup_obj = Mock() self.exportgroup_obj = mock.Mock()
self.exportgroup_obj.exportgroup_list.return_value = ( self.exportgroup_obj.exportgroup_list.return_value = (
export_group_list) export_group_list)
self.exportgroup_obj.exportgroup_show.return_value = ( self.exportgroup_obj.exportgroup_show.return_value = (
@ -356,7 +377,7 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
self.exportgroup_obj.exportgroup_add_volumes.return_value = ( self.exportgroup_obj.exportgroup_add_volumes.return_value = (
"volume-added") "volume-added")
self.host_obj = Mock() self.host_obj = mock.Mock()
self.host_obj.list_by_tenant.return_value = [] self.host_obj.list_by_tenant.return_value = []
self.host_obj.list_all.return_value = [{'id': "host1_id", self.host_obj.list_all.return_value = [{'id': "host1_id",
'name': "host1"}] 'name': "host1"}]
@ -365,11 +386,11 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
{'name': "12:34:56:78:90:54:32:11"}, {'name': "12:34:56:78:90:54:32:11"},
{'name': "bfdf432500000004"}] {'name': "bfdf432500000004"}]
self.hostinitiator_obj = Mock() self.hostinitiator_obj = mock.Mock()
self.varray_obj = Mock() self.varray_obj = mock.Mock()
self.varray_obj.varray_show.return_value = varray_detail_data 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 = self.snapshot_obj.return_value
mocked_snap_obj.storageResource_query.return_value = ( mocked_snap_obj.storageResource_query.return_value = (
"resourceUri") "resourceUri")
@ -377,10 +398,10 @@ class MockedEMCCoprHDDriverCommon(coprhd_common.EMCCoprHDDriverCommon):
"snapshot_created") "snapshot_created")
mocked_snap_obj.snapshot_query.return_value = "snapshot_uri" mocked_snap_obj.snapshot_query.return_value = "snapshot_uri"
self.consistencygroup_obj = Mock() self.consistencygroup_obj = mock.Mock()
mocked_cg_object = self.consistencygroup_obj.return_value mocked_group_object = self.consistencygroup_obj.return_value
mocked_cg_object.create.return_value = "CG-Created" mocked_group_object.create.return_value = "CG-Created"
mocked_cg_object.consistencygroup_query.return_value = "CG-uri" mocked_group_object.consistencygroup_query.return_value = "CG-uri"
class EMCCoprHDISCSIDriverTest(test.TestCase): class EMCCoprHDISCSIDriverTest(test.TestCase):
@ -391,7 +412,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
def create_coprhd_setup(self): def create_coprhd_setup(self):
self.configuration = Mock() self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443" self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDISCSIDriver" self.configuration.volume_backend_name = "EMCCoprHDISCSIDriver"
@ -402,7 +423,10 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
self.configuration.coprhd_varray = "varray" self.configuration.coprhd_varray = "varray"
self.configuration.coprhd_emulate_snapshot = False 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, self.mock_object(coprhd_iscsi.EMCCoprHDISCSIDriver,
'_get_common_driver', '_get_common_driver',
@ -423,8 +447,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
"coprhd-volume-type", "coprhd-volume-type",
{'CoprHD:VPOOL': {'CoprHD:VPOOL':
'vpool_coprhd'}) 'vpool_coprhd'})
volume_id = vipr_volume_type['id'] return vipr_volume_type
return volume_id
def _get_mocked_common_driver(self): def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon( return MockedEMCCoprHDDriverCommon(
@ -437,7 +460,7 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id) volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self): 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.create_volume(volume)
self.driver.delete_volume(volume) self.driver.delete_volume(volume)
@ -447,17 +470,17 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
self.assertEqual('unknown', vol_stats['free_capacity_gb']) self.assertEqual('unknown', vol_stats['free_capacity_gb'])
def test_create_volume_clone(self): def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id) src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_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_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, 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(src_volume_data)
self.driver.delete_volume(clone_volume_data) self.driver.delete_volume(clone_volume_data)
def test_create_destroy_snapshot(self): def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data( snapshot_data = test_snapshot_data(
get_source_test_volume_data(self.volume_type_id)) source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data) self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
@ -466,11 +489,11 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
def test_create_volume_from_snapshot(self): 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) self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data) snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, 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) self.driver.delete_volume(volume_data)
def test_extend_volume(self): 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.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2) self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_initialize_and_terminate_connection(self): def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data() 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) self.driver.create_volume(volume_data)
res_initialize = self.driver.initialize_connection( res_initialize = self.driver.initialize_connection(
@ -498,54 +521,63 @@ class EMCCoprHDISCSIDriverTest(test.TestCase):
'target_iqn': 'target_iqn':
'50:00:09:73:00:18:95:19', '50:00:09:73:00:18:95:19',
'target_discovered': False, 'target_discovered': False,
'volume_id': '1'}} 'volume_id': fake.VOLUME_ID}}
self.assertEqual( self.assertEqual(
expected_initialize, res_initialize, 'Unexpected return data') expected_initialize, res_initialize, 'Unexpected return data')
self.driver.terminate_connection(volume_data, connector_data) self.driver.terminate_connection(volume_data, connector_data)
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data) self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = ( 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') self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() 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) self.driver.create_volume(volume)
model_update, ret1, ret2 = ( 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)
model_update, volumes_model_update = ( model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume])) self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}], self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update) volumes_model_update)
def test_create_delete_CG_snap(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_snap_data = get_test_CG_snap_data(self.volume_type_id) 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() ctx = context.get_admin_context()
model_update, snapshots_model_update = ( model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = ( 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({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_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): def create_coprhd_setup(self):
self.configuration = Mock() self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443" self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDFCDriver" self.configuration.volume_backend_name = "EMCCoprHDFCDriver"
@ -569,7 +601,10 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.configuration.coprhd_varray = "varray" self.configuration.coprhd_varray = "varray"
self.configuration.coprhd_emulate_snapshot = False 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, self.mock_object(coprhd_fc.EMCCoprHDFCDriver,
'_get_common_driver', '_get_common_driver',
@ -589,8 +624,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
vipr_volume_type = volume_types.create(ctx, vipr_volume_type = volume_types.create(ctx,
"coprhd-volume-type", "coprhd-volume-type",
{'CoprHD:VPOOL': 'vpool_vipr'}) {'CoprHD:VPOOL': 'vpool_vipr'})
volume_id = vipr_volume_type['id'] return vipr_volume_type
return volume_id
def _get_mocked_common_driver(self): def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon( return MockedEMCCoprHDDriverCommon(
@ -603,7 +637,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id) volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self): 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.create_volume(volume)
self.driver.delete_volume(volume) self.driver.delete_volume(volume)
@ -614,8 +648,8 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_create_volume_clone(self): def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id) src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_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_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, 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(src_volume_data)
@ -623,9 +657,9 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_create_destroy_snapshot(self): def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data( snapshot_data = test_snapshot_data(
get_source_test_volume_data(self.volume_type_id)) source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data) self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
@ -633,11 +667,11 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_create_volume_from_snapshot(self): 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) self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data) snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, 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(src_vol_data)
self.driver.delete_volume(volume_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): 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.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2) self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
@ -669,7 +689,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
def test_initialize_and_terminate_connection(self): def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data() 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) self.driver.create_volume(volume_data)
res_initiatlize = self.driver.initialize_connection( res_initiatlize = self.driver.initialize_connection(
@ -686,7 +706,7 @@ class EMCCoprHDFCDriverTest(test.TestCase):
'target_wwn': ['1234567890123456', 'target_wwn': ['1234567890123456',
'1234567890123456'], '1234567890123456'],
'target_discovered': False, 'target_discovered': False,
'volume_id': '1'}} 'volume_id': fake.VOLUME_ID}}
self.assertEqual( self.assertEqual(
expected_initialize, res_initiatlize, 'Unexpected return data') expected_initialize, res_initiatlize, 'Unexpected return data')
@ -707,47 +727,56 @@ class EMCCoprHDFCDriverTest(test.TestCase):
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data) self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = ( 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') self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() 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) self.driver.create_volume(volume)
model_update, ret1, ret2 = ( 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)
model_update, volumes_model_update = ( model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume])) self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}], self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update) volumes_model_update)
def test_create_delete_CG_snap(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_snap_data = get_test_CG_snap_data(self.volume_type_id) 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() ctx = context.get_admin_context()
model_update, snapshots_model_update = ( model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = ( 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({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_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): def create_coprhd_setup(self):
self.configuration = Mock() self.configuration = mock.Mock()
self.configuration.coprhd_hostname = "10.10.10.10" self.configuration.coprhd_hostname = "10.10.10.10"
self.configuration.coprhd_port = "4443" self.configuration.coprhd_port = "4443"
self.configuration.volume_backend_name = "EMCCoprHDFCDriver" self.configuration.volume_backend_name = "EMCCoprHDFCDriver"
@ -779,7 +808,10 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
self.configuration.scaleio_server_certificate_path = ( self.configuration.scaleio_server_certificate_path = (
"/etc/scaleio/certs") "/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, self.mock_object(coprhd_scaleio.EMCCoprHDScaleIODriver,
'_get_common_driver', '_get_common_driver',
@ -802,8 +834,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
vipr_volume_type = volume_types.create(ctx, vipr_volume_type = volume_types.create(ctx,
"coprhd-volume-type", "coprhd-volume-type",
{'CoprHD:VPOOL': 'vpool_vipr'}) {'CoprHD:VPOOL': 'vpool_vipr'})
volume_id = vipr_volume_type['id'] return vipr_volume_type
return volume_id
def _get_mocked_common_driver(self): def _get_mocked_common_driver(self):
return MockedEMCCoprHDDriverCommon( return MockedEMCCoprHDDriverCommon(
@ -820,7 +851,7 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
volume_types.destroy(ctx, self.volume_type_id) volume_types.destroy(ctx, self.volume_type_id)
def test_create_destroy(self): 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.create_volume(volume)
self.driver.delete_volume(volume) self.driver.delete_volume(volume)
@ -831,8 +862,8 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_create_volume_clone(self): def test_create_volume_clone(self):
src_volume_data = get_test_volume_data(self.volume_type_id) src_volume_data = test_volume_data(self.volume_type_id)
clone_volume_data = get_clone_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_volume(src_volume_data)
self.driver.create_cloned_volume(clone_volume_data, 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(src_volume_data)
@ -840,9 +871,9 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_create_destroy_snapshot(self): def test_create_destroy_snapshot(self):
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data( snapshot_data = test_snapshot_data(
get_source_test_volume_data(self.volume_type_id)) source_test_volume_data(self.volume_type_id))
self.driver.create_volume(volume_data) self.driver.create_volume(volume_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
@ -850,11 +881,11 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_create_volume_from_snapshot(self): 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) self.driver.create_volume(src_vol_data)
volume_data = get_test_volume_data(self.volume_type_id) volume_data = test_volume_data(self.volume_type_id)
snapshot_data = get_test_snapshot_data(src_vol_data) snapshot_data = test_snapshot_data(src_vol_data)
self.driver.create_snapshot(snapshot_data) self.driver.create_snapshot(snapshot_data)
self.driver.create_volume_from_snapshot(volume_data, 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) self.driver.delete_volume(volume_data)
def test_extend_volume(self): 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.create_volume(volume_data)
self.driver.extend_volume(volume_data, 2) self.driver.extend_volume(volume_data, 2)
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
@ -872,16 +903,17 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
def test_initialize_and_terminate_connection(self): def test_initialize_and_terminate_connection(self):
connector_data = get_connector_data() 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) self.driver.create_volume(volume_data)
res_initiatlize = self.driver.initialize_connection( res_initiatlize = self.driver.initialize_connection(
volume_data, connector_data) volume_data, connector_data)
exp_name = res_initiatlize['data']['scaleIO_volname']
expected_initialize = {'data': {'bandwidthLimit': None, expected_initialize = {'data': {'bandwidthLimit': None,
'hostIP': '10.0.0.2', 'hostIP': '10.0.0.2',
'iopsLimit': None, 'iopsLimit': None,
'scaleIO_volname': 'test-vol1', 'scaleIO_volname': exp_name,
'scaleIO_volume_id': '1', 'scaleIO_volume_id': fake.PROVIDER_ID,
'serverIP': '10.10.10.11', 'serverIP': '10.10.10.11',
'serverPassword': 'scaleio_password', 'serverPassword': 'scaleio_password',
'serverPort': 443, 'serverPort': 443,
@ -895,46 +927,55 @@ class EMCCoprHDScaleIODriverTest(test.TestCase):
volume_data, connector_data) volume_data, connector_data)
self.driver.delete_volume(volume_data) self.driver.delete_volume(volume_data)
def test_create_delete_empty_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() ctx = context.get_admin_context()
self.driver.create_consistencygroup(ctx, cg_data) self.driver.create_group(ctx, group_data)
model_update, volumes_model_update = ( 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') self.assertEqual([], volumes_model_update, 'Unexpected return data')
def test_create_update_delete_CG(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_data = get_test_CG_data(self.volume_type_id) 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() 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) self.driver.create_volume(volume)
model_update, ret1, ret2 = ( 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)
model_update, volumes_model_update = ( model_update, volumes_model_update = (
self.driver.delete_consistencygroup(ctx, cg_data, [volume])) self.driver.delete_group(ctx, group_data, [volume]))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([{'status': 'deleted', 'id': '1'}], self.assertEqual([{'status': 'deleted', 'id': fake.VOLUME_ID}],
volumes_model_update) volumes_model_update)
def test_create_delete_CG_snap(self): @mock.patch('cinder.volume.utils.is_group_a_cg_snapshot_type')
cg_snap_data = get_test_CG_snap_data(self.volume_type_id) 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() ctx = context.get_admin_context()
model_update, snapshots_model_update = ( model_update, snapshots_model_update = (
self.driver.create_cgsnapshot(ctx, cg_snap_data, [])) self.driver.create_group_snapshot(ctx, group_snap_data, []))
self.assertEqual({'status': fields.ConsistencyGroupStatus.AVAILABLE}, self.assertEqual({'status': fields.GroupStatus.AVAILABLE},
model_update) model_update)
self.assertEqual([], snapshots_model_update, 'Unexpected return data') self.assertEqual([], snapshots_model_update, 'Unexpected return data')
model_update, snapshots_model_update = ( 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({}, model_update, 'Unexpected return data')
self.assertEqual([], snapshots_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 ( from cinder.volume.drivers.coprhd.helpers import (
virtualarray as coprhd_varray) virtualarray as coprhd_varray)
from cinder.volume.drivers.coprhd.helpers import volume as coprhd_vol 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 from cinder.volume import volume_types
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
MAX_RETRIES = 10 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_VPOOL_VARRAY_CAPACITY = '/block/vpools/{0}/varrays/{1}/capacity'
URI_BLOCK_EXPORTS_FOR_INITIATORS = '/block/exports?initiators={0}' URI_BLOCK_EXPORTS_FOR_INITIATORS = '/block/exports?initiators={0}'
EXPORT_RETRY_COUNT = 5 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): def retry_wrapper(func):
@ -225,8 +229,9 @@ class EMCCoprHDDriverCommon(object):
def create_volume(self, vol, driver, truncate_name=False): def create_volume(self, vol, driver, truncate_name=False):
self.authenticate_user() self.authenticate_user()
name = self._get_resource_name(vol, truncate_name) name = self._get_resource_name(vol, MAX_DEFAULT_NAME_LENGTH,
size = int(vol['size']) * units.Gi truncate_name)
size = int(vol.size) * units.Gi
vpool = self._get_vpool(vol) vpool = self._get_vpool(vol)
self.vpool = vpool['CoprHD:VPOOL'] self.vpool = vpool['CoprHD:VPOOL']
@ -234,14 +239,17 @@ class EMCCoprHDDriverCommon(object):
try: try:
coprhd_cgid = None coprhd_cgid = None
try: try:
cgid = vol['consistencygroup_id'] if vol.group_id:
if cgid: if volume_utils.is_group_a_cg_snapshot_type(vol.group):
coprhd_cgid = self._get_coprhd_cgid(cgid) coprhd_cgid = self._get_coprhd_cgid(vol.group_id)
except KeyError: except KeyError:
coprhd_cgid = None coprhd_cgid = None
except AttributeError:
coprhd_cgid = None
full_project_name = ("%s/%s" % (self.configuration.coprhd_tenant, 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.volume_obj.create(full_project_name, name, size,
self.configuration.coprhd_varray, self.configuration.coprhd_varray,
self.vpool, self.vpool,
@ -249,6 +257,7 @@ class EMCCoprHDDriverCommon(object):
sync=True, sync=True,
# no longer specified in volume creation # no longer specified in volume creation
consistencygroup=coprhd_cgid) consistencygroup=coprhd_cgid)
except coprhd_utils.CoprHdError as e: except coprhd_utils.CoprHdError as e:
coprhd_err_msg = (_("Volume %(name)s: create failed\n%(err)s") % coprhd_err_msg = (_("Volume %(name)s: create failed\n%(err)s") %
{'name': name, 'err': six.text_type(e.msg)}) {'name': name, 'err': six.text_type(e.msg)})
@ -260,7 +269,9 @@ class EMCCoprHDDriverCommon(object):
@retry_wrapper @retry_wrapper
def create_consistencygroup(self, context, group, truncate_name=False): def create_consistencygroup(self, context, group, truncate_name=False):
self.authenticate_user() 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: try:
self.consistencygroup_obj.create( self.consistencygroup_obj.create(
@ -291,8 +302,8 @@ class EMCCoprHDDriverCommon(object):
def update_consistencygroup(self, group, add_volumes, def update_consistencygroup(self, group, add_volumes,
remove_volumes): remove_volumes):
self.authenticate_user() self.authenticate_user()
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} model_update = {'status': fields.GroupStatus.AVAILABLE}
cg_uri = self._get_coprhd_cgid(group['id']) cg_uri = self._get_coprhd_cgid(group.id)
add_volnames = [] add_volnames = []
remove_volnames = [] remove_volnames = []
@ -329,7 +340,9 @@ class EMCCoprHDDriverCommon(object):
def delete_consistencygroup(self, context, group, volumes, def delete_consistencygroup(self, context, group, volumes,
truncate_name=False): truncate_name=False):
self.authenticate_user() 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 = [] volumes_model_update = []
try: try:
@ -344,20 +357,20 @@ class EMCCoprHDDriverCommon(object):
sync=True, sync=True,
force_delete=True) force_delete=True)
update_item = {'id': vol['id'], update_item = {'id': vol.id,
'status': 'status':
fields.ConsistencyGroupStatus.DELETED} fields.GroupStatus.DELETED}
volumes_model_update.append(update_item) volumes_model_update.append(update_item)
except exception.VolumeBackendAPIException: except exception.VolumeBackendAPIException:
update_item = {'id': vol['id'], update_item = {'id': vol.id,
'status': fields.ConsistencyGroupStatus. 'status': fields.ConsistencyGroupStatus.
ERROR_DELETING} ERROR_DELETING}
volumes_model_update.append(update_item) volumes_model_update.append(update_item)
LOG.exception("Failed to delete the volume %s of CG.", LOG.exception("Failed to delete the volume %s of CG.",
vol['name']) vol.name)
self.consistencygroup_obj.delete( self.consistencygroup_obj.delete(
name, name,
@ -365,7 +378,7 @@ class EMCCoprHDDriverCommon(object):
self.configuration.coprhd_tenant) self.configuration.coprhd_tenant)
model_update = {} model_update = {}
model_update['status'] = group['status'] model_update['status'] = group.status
return model_update, volumes_model_update return model_update, volumes_model_update
@ -384,9 +397,19 @@ class EMCCoprHDDriverCommon(object):
self.authenticate_user() self.authenticate_user()
snapshots_model_update = [] snapshots_model_update = []
cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name) cgsnapshot_name = self._get_resource_name(cgsnapshot,
cg_id = cgsnapshot['consistencygroup_id'] MAX_SNAPSHOT_NAME_LENGTH,
cg_group = cgsnapshot.get('consistencygroup') truncate_name)
cg_id = None
cg_group = None
try:
cg_id = cgsnapshot.group_id
cg_group = cgsnapshot.group
except AttributeError:
pass
cg_name = None cg_name = None
coprhd_cgid = None coprhd_cgid = None
@ -408,7 +431,7 @@ class EMCCoprHDDriverCommon(object):
True) True)
for snapshot in snapshots: 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 # Finding the volume in CoprHD for this volume id
tagname = "OpenStack:id:" + vol_id_of_snap tagname = "OpenStack:id:" + vol_id_of_snap
@ -470,7 +493,7 @@ class EMCCoprHDDriverCommon(object):
snapshot['status'] = fields.SnapshotStatus.AVAILABLE snapshot['status'] = fields.SnapshotStatus.AVAILABLE
snapshots_model_update.append( snapshots_model_update.append(
{'id': snapshot['id'], 'status': {'id': snapshot.id, 'status':
fields.SnapshotStatus.AVAILABLE}) fields.SnapshotStatus.AVAILABLE})
model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE} model_update = {'status': fields.ConsistencyGroupStatus.AVAILABLE}
@ -493,19 +516,28 @@ class EMCCoprHDDriverCommon(object):
@retry_wrapper @retry_wrapper
def delete_cgsnapshot(self, cgsnapshot, snapshots, truncate_name=False): def delete_cgsnapshot(self, cgsnapshot, snapshots, truncate_name=False):
self.authenticate_user() self.authenticate_user()
cgsnapshot_id = cgsnapshot['id'] cgsnapshot_id = cgsnapshot.id
cgsnapshot_name = self._get_resource_name(cgsnapshot, truncate_name) cgsnapshot_name = self._get_resource_name(cgsnapshot,
MAX_SNAPSHOT_NAME_LENGTH,
truncate_name)
snapshots_model_update = [] 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) coprhd_cgid = self._get_coprhd_cgid(cg_id)
cg_name = self._get_consistencygroup_name(cg_group) cg_name = self._get_consistencygroup_name(cg_group)
model_update = {} model_update = {}
LOG.info('Delete cgsnapshot %(snap_name)s for consistency group: ' 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}) 'group_name': cg_name})
try: try:
@ -531,7 +563,7 @@ class EMCCoprHDDriverCommon(object):
for snapshot in snapshots: for snapshot in snapshots:
snapshots_model_update.append( snapshots_model_update.append(
{'id': snapshot['id'], {'id': snapshot.id,
'status': fields.SnapshotStatus.DELETED}) 'status': fields.SnapshotStatus.DELETED})
return model_update, snapshots_model_update return model_update, snapshots_model_update
@ -557,7 +589,9 @@ class EMCCoprHDDriverCommon(object):
exempt_tags = [] exempt_tags = []
self.authenticate_user() 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" % ( full_project_name = ("%s/%s" % (
self.configuration.coprhd_tenant, self.configuration.coprhd_tenant,
self.configuration.coprhd_project)) self.configuration.coprhd_project))
@ -613,11 +647,16 @@ class EMCCoprHDDriverCommon(object):
if ((not prop.startswith("status") and not if ((not prop.startswith("status") and not
prop.startswith("obj_status") and prop.startswith("obj_status") and
prop != "obj_volume") and value): prop != "obj_volume") and value):
add_tags.append( tag = ("%s:%s:%s" %
"%s:%s:%s" % (self.OPENSTACK_TAG, prop, (self.OPENSTACK_TAG, prop,
six.text_type(value))) six.text_type(value)))
if len(tag) > 128:
tag = tag[0:128]
add_tags.append(tag)
except TypeError: except TypeError:
LOG.error("Error tagging the resource property %s", prop) LOG.error(
"Error tagging the resource property %s", prop)
except TypeError: except TypeError:
LOG.error("Error tagging the resource properties") 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): def create_cloned_volume(self, vol, src_vref, truncate_name=False):
"""Creates a clone of the specified volume.""" """Creates a clone of the specified volume."""
self.authenticate_user() 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) srcname = self._get_coprhd_volume_name(src_vref)
try: try:
if src_vref['consistencygroup_id']: if src_vref.group_id:
raise coprhd_utils.CoprHdError( raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR, coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Clone can't be taken individually on a volume" _("Clone can't be taken individually on a volume"
" that is part of a Consistency Group")) " that is part of a Consistency Group"))
except KeyError as e: except KeyError as e:
pass pass
except AttributeError:
pass
try: try:
(storageres_type, (storageres_type,
storageres_typename) = self.volume_obj.get_storageAttributes( 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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
try: src_vol_size = 0
src_vol_size = src_vref['size'] dest_vol_size = 0
except KeyError:
src_vol_size = src_vref['volume_size']
if vol['size'] > src_vol_size: try:
size_in_bytes = coprhd_utils.to_bytes("%sG" % vol['size']) 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: try:
self.volume_obj.expand( self.volume_obj.expand(
("%s/%s" % (self.configuration.coprhd_tenant, ("%s/%s" % (self.configuration.coprhd_tenant,
@ -733,7 +784,8 @@ class EMCCoprHDDriverCommon(object):
{'volume_name': volume_name, {'volume_name': volume_name,
'err': six.text_type(e.msg)}) '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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
@ -747,15 +799,20 @@ class EMCCoprHDDriverCommon(object):
self.create_cloned_volume(volume, snapshot, truncate_name) self.create_cloned_volume(volume, snapshot, truncate_name)
return return
if snapshot.get('cgsnapshot_id'): try:
if snapshot.group_snapshot_id:
raise coprhd_utils.CoprHdError( raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR, coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Volume cannot be created individually from a snapshot " _("Volume cannot be created individually from a snapshot "
"that is part of a Consistency Group")) "that is part of a Consistency Group"))
except AttributeError:
pass
src_snapshot_name = None src_snapshot_name = None
src_vol_ref = snapshot['volume'] src_vol_ref = snapshot.volume
new_volume_name = self._get_resource_name(volume, truncate_name) new_volume_name = self._get_resource_name(volume,
MAX_DEFAULT_NAME_LENGTH,
truncate_name)
try: try:
coprhd_vol_info = self._get_coprhd_volume_name( coprhd_vol_info = self._get_coprhd_volume_name(
@ -786,12 +843,13 @@ class EMCCoprHDDriverCommon(object):
{'src_snapshot_name': src_snapshot_name, {'src_snapshot_name': src_snapshot_name,
'err': six.text_type(e.msg)}) '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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
if volume['size'] > snapshot['volume_size']: if volume.size > snapshot.volume_size:
size_in_bytes = coprhd_utils.to_bytes("%sG" % volume['size']) size_in_bytes = coprhd_utils.to_bytes("%sG" % volume.size)
try: try:
self.volume_obj.expand( self.volume_obj.expand(
@ -805,7 +863,8 @@ class EMCCoprHDDriverCommon(object):
{'volume_name': new_volume_name, {'volume_name': new_volume_name,
'err': six.text_type(e.msg)}) '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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
@ -829,7 +888,7 @@ class EMCCoprHDDriverCommon(object):
"\n%(err)s") % "\n%(err)s") %
{'name': name, 'err': six.text_type(e.msg)}) {'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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
@ -837,10 +896,10 @@ class EMCCoprHDDriverCommon(object):
def create_snapshot(self, snapshot, truncate_name=False): def create_snapshot(self, snapshot, truncate_name=False):
self.authenticate_user() self.authenticate_user()
volume = snapshot['volume'] volume = snapshot.volume
try: try:
if volume['consistencygroup_id']: if volume.group_id:
raise coprhd_utils.CoprHdError( raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR, coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Snapshot can't be taken individually on a volume" _("Snapshot can't be taken individually on a volume"
@ -855,8 +914,10 @@ class EMCCoprHDDriverCommon(object):
return return
try: try:
snapshotname = self._get_resource_name(snapshot, truncate_name) snapshotname = self._get_resource_name(snapshot,
vol = snapshot['volume'] MAX_SNAPSHOT_NAME_LENGTH,
truncate_name)
vol = snapshot.volume
volumename = self._get_coprhd_volume_name(vol) volumename = self._get_coprhd_volume_name(vol)
projectname = self.configuration.coprhd_project projectname = self.configuration.coprhd_project
@ -894,7 +955,7 @@ class EMCCoprHDDriverCommon(object):
"\n%(err)s") % {'snapshotname': snapshotname, "\n%(err)s") % {'snapshotname': snapshotname,
'err': six.text_type(e.msg)}) '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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
@ -902,10 +963,10 @@ class EMCCoprHDDriverCommon(object):
def delete_snapshot(self, snapshot): def delete_snapshot(self, snapshot):
self.authenticate_user() self.authenticate_user()
vol = snapshot['volume'] vol = snapshot.volume
try: try:
if vol['consistencygroup_id']: if vol.group_id:
raise coprhd_utils.CoprHdError( raise coprhd_utils.CoprHdError(
coprhd_utils.CoprHdError.SOS_FAILURE_ERR, coprhd_utils.CoprHdError.SOS_FAILURE_ERR,
_("Snapshot delete can't be done individually on a volume" _("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") % coprhd_err_msg = (_("Snapshot %s : Delete Failed\n") %
snapshotname) 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, self._raise_or_log_exception(e.err_code, coprhd_err_msg,
log_err_msg) log_err_msg)
@ -1170,7 +1231,7 @@ class EMCCoprHDDriverCommon(object):
(_("Consistency Group %s not found") % cgid)) (_("Consistency Group %s not found") % cgid))
def _get_consistencygroup_name(self, consisgrp): def _get_consistencygroup_name(self, consisgrp):
return consisgrp['name'] return consisgrp.name
def _get_coprhd_snapshot_name(self, snapshot, resUri): def _get_coprhd_snapshot_name(self, snapshot, resUri):
tagname = self.OPENSTACK_TAG + ":id:" + snapshot['id'] tagname = self.OPENSTACK_TAG + ":id:" + snapshot['id']
@ -1200,7 +1261,7 @@ class EMCCoprHDDriverCommon(object):
return rslt_snap['name'] return rslt_snap['name']
def _get_coprhd_volume_name(self, vol, verbose=False): 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( rslt = coprhd_utils.search_by_tag(
coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname), coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname),
self.configuration.coprhd_hostname, self.configuration.coprhd_hostname,
@ -1210,7 +1271,7 @@ class EMCCoprHDDriverCommon(object):
# as "OpenStack:obj_id" # as "OpenStack:obj_id"
# as snapshots will be having the obj_id instead of just id. # as snapshots will be having the obj_id instead of just id.
if len(rslt) == 0: 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( rslt = coprhd_utils.search_by_tag(
coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname), coprhd_vol.Volume.URI_SEARCH_VOLUMES_BY_TAG.format(tagname),
self.configuration.coprhd_hostname, self.configuration.coprhd_hostname,
@ -1228,21 +1289,36 @@ class EMCCoprHDDriverCommon(object):
coprhd_utils.CoprHdError.NOT_FOUND_ERR, coprhd_utils.CoprHdError.NOT_FOUND_ERR,
(_("Volume %s not found") % vol['display_name'])) (_("Volume %s not found") % vol['display_name']))
def _get_resource_name(self, resource, truncate_name=False): def _get_resource_name(self, resource,
name = resource.get('display_name', None) 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: 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) 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): def _get_vpool(self, volume):
vpool = {} vpool = {}
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
type_id = volume['volume_type_id'] type_id = volume.volume_type_id
if type_id is not None: if type_id is not None:
volume_type = volume_types.get_volume_type(ctxt, type_id) volume_type = volume_types.get_volume_type(ctxt, type_id)
specs = volume_type.get('extra_specs') specs = volume_type.get('extra_specs')
@ -1363,7 +1439,8 @@ class EMCCoprHDDriverCommon(object):
self.authenticate_user() self.authenticate_user()
try: try:
self.stats['consistencygroup_support'] = 'True' self.stats['consistencygroup_support'] = True
self.stats['consistent_group_snapshot_enabled'] = True
vols = self.volume_obj.list_volumes( vols = self.volume_obj.list_volumes(
self.configuration.coprhd_tenant + self.configuration.coprhd_tenant +
"/" + "/" +

View File

@ -20,9 +20,13 @@ import re
from oslo_log import log as logging from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface from cinder import interface
from cinder.volume import driver from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common 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 from cinder.zonemanager import utils as fczm_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -89,30 +93,67 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
pass pass
def remove_export(self, context, volume): 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 pass
def create_consistencygroup(self, context, group): def create_group(self, context, group):
"""Creates a consistencygroup.""" """Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group) return self.common.create_consistencygroup(context, group)
def update_consistencygroup(self, context, group, add_volumes=None, # If the group is not consistency group snapshot enabled, then
# we shall rely on generic volume group implementation
raise NotImplementedError()
def update_group(self, context, group, add_volumes=None,
remove_volumes=None): remove_volumes=None):
"""Updates volumes in consistency group.""" """Updates volumes in group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.update_consistencygroup(group, add_volumes, return self.common.update_consistencygroup(group, add_volumes,
remove_volumes) remove_volumes)
def delete_consistencygroup(self, context, group, volumes): # If the group is not consistency group snapshot enabled, then
"""Deletes a consistency group.""" # we shall rely on generic volume group implementation
raise NotImplementedError()
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) return self.common.delete_consistencygroup(context, group, volumes)
def create_cgsnapshot(self, context, cgsnapshot, snapshots): # If the group is not consistency group snapshot enabled, then
"""Creates a cgsnapshot.""" # we shall rely on generic volume group implementation
return self.common.create_cgsnapshot(cgsnapshot, snapshots) raise NotImplementedError()
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a cgsnapshot.""" """Creates a group snapshot."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots) 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): def check_for_export(self, context, volume_id):
"""Make sure volume is exported.""" """Make sure volume is exported."""
@ -123,14 +164,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
"""Initializes the connection and returns connection info.""" """Initializes the connection and returns connection info."""
properties = {} properties = {}
properties['volume_id'] = volume['id'] properties['volume_id'] = volume.id
properties['target_discovered'] = False properties['target_discovered'] = False
properties['target_wwn'] = [] properties['target_wwn'] = []
init_ports = self._build_initport_list(connector) init_ports = self._build_initport_list(connector)
itls = self.common.initialize_connection(volume, itls = self.common.initialize_connection(volume, 'FC', init_ports,
'FC',
init_ports,
connector['host']) connector['host'])
target_wwns = None target_wwns = None
@ -144,7 +183,12 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
properties['target_wwn'] = target_wwns properties['target_wwn'] = target_wwns
properties['initiator_target_map'] = initiator_target_map properties['initiator_target_map'] = initiator_target_map
auth = volume['provider_auth'] auth = None
try:
auth = volume.provider_auth
except AttributeError:
pass
if auth: if auth:
(auth_method, auth_username, auth_secret) = auth.split() (auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method properties['auth_method'] = auth_method
@ -162,9 +206,7 @@ class EMCCoprHDFCDriver(driver.FibreChannelDriver):
"""Driver entry point to detach a volume from an instance.""" """Driver entry point to detach a volume from an instance."""
init_ports = self._build_initport_list(connector) init_ports = self._build_initport_list(connector)
itls = self.common.terminate_connection(volume, itls = self.common.terminate_connection(volume, 'FC', init_ports,
'FC',
init_ports,
connector['host']) connector['host'])
volumes_count = self.common.get_exports_count_by_initiators(init_ports) 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 oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder import interface from cinder import interface
from cinder.volume import driver from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -67,7 +69,7 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
self.common.expand_volume(volume, new_size) self.common.expand_volume(volume, new_size)
def delete_volume(self, volume): def delete_volume(self, volume):
"""Deletes an volume.""" """Deletes a volume."""
self.common.delete_volume(volume) self.common.delete_volume(volume)
def create_snapshot(self, snapshot): def create_snapshot(self, snapshot):
@ -90,27 +92,65 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
"""Driver entry point to remove an export for a volume.""" """Driver entry point to remove an export for a volume."""
pass pass
def create_consistencygroup(self, context, group): def create_group(self, context, group):
"""Creates a consistencygroup.""" """Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group) return self.common.create_consistencygroup(context, group)
def delete_consistencygroup(self, context, group, volumes): # If the group is not consistency group snapshot enabled, then
"""Deletes a consistency group.""" # we shall rely on generic volume group implementation
return self.common.delete_consistencygroup(context, group, volumes) raise NotImplementedError()
def update_consistencygroup(self, context, group, def create_group_from_src(self, ctxt, group, volumes,
add_volumes=None, remove_volumes=None): group_snapshot=None, snapshots=None,
"""Updates volumes in consistency group.""" 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 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, return self.common.update_consistencygroup(group, add_volumes,
remove_volumes) remove_volumes)
def create_cgsnapshot(self, context, cgsnapshot, snapshots): # If the group is not consistency group snapshot enabled, then
"""Creates a cgsnapshot.""" # we shall rely on generic volume group implementation
return self.common.create_cgsnapshot(cgsnapshot, snapshots) raise NotImplementedError()
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): def delete_group(self, context, group, volumes):
"""Deletes a cgsnapshot.""" """Deletes a group."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots) 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): def check_for_export(self, context, volume_id):
"""Make sure volume is exported.""" """Make sure volume is exported."""
@ -127,14 +167,20 @@ class EMCCoprHDISCSIDriver(driver.ISCSIDriver):
connector['host']) connector['host'])
properties = {} properties = {}
properties['target_discovered'] = False properties['target_discovered'] = False
properties['volume_id'] = volume['id'] properties['volume_id'] = volume.id
if itls: if itls:
properties['target_iqn'] = itls[0]['target']['port'] properties['target_iqn'] = itls[0]['target']['port']
properties['target_portal'] = '%s:%s' % ( properties['target_portal'] = '%s:%s' % (
itls[0]['target']['ip_address'], itls[0]['target']['ip_address'],
itls[0]['target']['tcp_port']) itls[0]['target']['tcp_port'])
properties['target_lun'] = itls[0]['hlu'] properties['target_lun'] = itls[0]['hlu']
auth = volume['provider_auth']
auth = None
try:
auth = volume.provider_auth
except AttributeError:
pass
if auth: if auth:
(auth_method, auth_username, auth_secret) = auth.split() (auth_method, auth_username, auth_secret) = auth.split()
properties['auth_method'] = auth_method properties['auth_method'] = auth_method

View File

@ -29,6 +29,7 @@ from cinder import interface
from cinder.volume import configuration from cinder.volume import configuration
from cinder.volume import driver from cinder.volume import driver
from cinder.volume.drivers.coprhd import common as coprhd_common from cinder.volume.drivers.coprhd import common as coprhd_common
from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -92,7 +93,7 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
"""Creates a Volume.""" """Creates a Volume."""
self.common.create_volume(volume, self, True) self.common.create_volume(volume, self, True)
self.common.set_volume_tags(volume, ['_obj_volume_type'], 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} return {'size': vol_size}
def _update_volume_size(self, 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.""" """Driver exntry point to remove an export for a volume."""
pass pass
def create_consistencygroup(self, context, group): def create_group(self, context, group):
"""Creates a consistencygroup.""" """Creates a group."""
if volume_utils.is_group_a_cg_snapshot_type(group):
return self.common.create_consistencygroup(context, group, True) return self.common.create_consistencygroup(context, group, True)
def update_consistencygroup(self, context, group, # If the group is not consistency group snapshot enabled, then
add_volumes=None, remove_volumes=None): # we shall rely on generic volume group implementation
"""Updates volumes in consistency group.""" raise NotImplementedError()
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, return self.common.update_consistencygroup(group, add_volumes,
remove_volumes) remove_volumes)
def delete_consistencygroup(self, context, group, volumes): # If the group is not consistency group snapshot enabled, then
"""Deletes a consistency group.""" # we shall rely on generic volume group implementation
raise NotImplementedError()
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, return self.common.delete_consistencygroup(context, group,
volumes, True) volumes, True)
def create_cgsnapshot(self, context, cgsnapshot, snapshots): # If the group is not consistency group snapshot enabled, then
"""Creates a cgsnapshot.""" # we shall rely on generic volume group implementation
return self.common.create_cgsnapshot(cgsnapshot, snapshots, True) raise NotImplementedError()
def delete_cgsnapshot(self, context, cgsnapshot, snapshots): def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Deletes a cgsnapshot.""" """Creates a group snapshot."""
return self.common.delete_cgsnapshot(cgsnapshot, snapshots, True) 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): def check_for_export(self, context, volume_id):
"""Make sure volume is exported.""" """Make sure volume is exported."""
@ -171,11 +212,13 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
def initialize_connection(self, volume, connector): def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info.""" """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 = {}
properties['scaleIO_volname'] = volname properties['scaleIO_volname'] = volname
properties['scaleIO_volume_id'] = volume['provider_id'] properties['scaleIO_volume_id'] = volume.provider_id
properties['hostIP'] = connector['ip'] properties['hostIP'] = connector['ip']
properties[ properties[
'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host 'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host
@ -215,10 +258,10 @@ class EMCCoprHDScaleIODriver(driver.VolumeDriver):
def terminate_connection(self, volume, connector, **kwargs): def terminate_connection(self, volume, connector, **kwargs):
"""Disallow connection from connector.""" """Disallow connection from connector."""
volname = volume['display_name'] volname = volume.display_name
properties = {} properties = {}
properties['scaleIO_volname'] = volname properties['scaleIO_volname'] = volname
properties['scaleIO_volume_id'] = volume['provider_id'] properties['scaleIO_volume_id'] = volume.provider_id
properties['hostIP'] = connector['ip'] properties['hostIP'] = connector['ip']
properties[ properties[
'serverIP'] = self.configuration.coprhd_scaleio_rest_gateway_host '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.