Add CG capability to generic groups in Huawei driver

This patch adds the consistency group capability to generic group in
Huawei driver. Consistency groups will be used as generic groups.

DocImpact:
Implements: blueprint huawei-generic-group
Change-Id: I16937c0ccd9f5a4ffad79f35c49ab32ee6092620
This commit is contained in:
huyang 2017-03-23 17:52:58 +08:00
parent d1b5a11325
commit 2e06995ad5
5 changed files with 182 additions and 145 deletions

View File

@ -25,9 +25,10 @@ from xml.dom import minidom
from cinder import context
from cinder import exception
from cinder.objects import fields
from cinder import test
from cinder.tests.unit.consistencygroup import fake_cgsnapshot
from cinder.tests.unit.consistencygroup import fake_consistencygroup
from cinder.tests.unit import fake_group
from cinder.tests.unit import fake_group_snapshot
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
from cinder.tests.unit import utils
@ -42,6 +43,7 @@ from cinder.volume.drivers.huawei import replication
from cinder.volume.drivers.huawei import rest_client
from cinder.volume.drivers.huawei import smartx
from cinder.volume import qos_specs
from cinder.volume import utils as volume_utils
from cinder.volume import volume_types
admin_contex = context.get_admin_context()
@ -382,7 +384,7 @@ FAKE_POOLS_SUPPORT_REPORT = {
'max_over_subscription_ratio': 20.0,
'luncopy': True,
'hypermetro': True,
'consistencygroup_support': True
'consistent_group_snapshot_enabled': True
}
FAKE_LUN_GET_SUCCESS_RESPONSE = """
@ -2020,6 +2022,15 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/mappingview/associate/portgroup?TYPE=245&ASSOC'
REPLICA_BACKEND_ID = 'huawei-replica-1'
def cg_or_cg_snapshot(func):
def wrapper(self, *args, **kwargs):
self.mock_object(volume_utils,
'is_group_a_cg_snapshot_type',
return_value=True)
return func(self, *args, **kwargs)
return wrapper
class FakeHuaweiConf(huawei_conf.HuaweiConf):
def __init__(self, conf, protocol):
self.conf = conf
@ -2241,10 +2252,10 @@ class HuaweiTestBase(test.TestCase):
admin_contex, id=ID, provider_location=PROVIDER_LOCATION,
name_id=ID)
self.cgsnapshot = fake_cgsnapshot.fake_cgsnapshot_obj(
admin_contex, id=ID, consistencygroup_id=ID, status='available')
self.group_snapshot = fake_group_snapshot.fake_group_snapshot_obj(
admin_contex, id=ID, group_id=ID, status='available')
self.cg = fake_consistencygroup.fake_consistencyobject_obj(
self.group = fake_group.fake_group_obj(
admin_contex, id=ID, status='available')
def test_encode_name(self):
@ -4286,49 +4297,52 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
iqn = self.driver.client._get_tgt_iqn_from_rest(ip)
self.assertIsNone(iqn)
def test_create_cgsnapshot(self):
@cg_or_cg_snapshot
def test_create_group_snapshot(self):
test_snapshots = [self.snapshot]
ctxt = context.get_admin_context()
model, snapshots = self.driver.create_cgsnapshot(ctxt,
self.cgsnapshot,
test_snapshots)
model, snapshots = (
self.driver.create_group_snapshot(ctxt, self.group_snapshot,
test_snapshots))
snapshots_model_update = [{'id': '21ec7341-9256-497b-97d9'
'-ef48edcf0635',
'status': 'available',
'provider_location': 11}]
self.assertEqual(snapshots_model_update, snapshots)
self.assertEqual('available', model['status'])
self.assertEqual(fields.GroupSnapshotStatus.AVAILABLE, model['status'])
def test_create_cgsnapshot_create_snapshot_fail(self):
@cg_or_cg_snapshot
def test_create_group_snapshot_with_create_snapshot_fail(self):
test_snapshots = [self.snapshot]
ctxt = context.get_admin_context()
self.mock_object(rest_client.RestClient, 'create_snapshot',
side_effect=(
exception.VolumeBackendAPIException(data='err')))
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cgsnapshot,
self.driver.create_group_snapshot,
ctxt,
self.cgsnapshot,
self.group_snapshot,
test_snapshots)
def test_create_cgsnapshot_active_snapshot_fail(self):
@cg_or_cg_snapshot
def test_create_group_snapshot_with_active_snapshot_fail(self):
test_snapshots = [self.snapshot]
ctxt = context.get_admin_context()
self.mock_object(rest_client.RestClient, 'activate_snapshot',
side_effect=(
exception.VolumeBackendAPIException(data='err')))
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cgsnapshot,
self.driver.create_group_snapshot,
ctxt,
self.cgsnapshot,
self.group_snapshot,
test_snapshots)
def test_delete_cgsnapshot(self):
@cg_or_cg_snapshot
def test_delete_group_snapshot(self):
test_snapshots = [self.snapshot]
ctxt = context.get_admin_context()
self.driver.delete_cgsnapshot(ctxt,
self.cgsnapshot,
test_snapshots)
self.driver.delete_group_snapshot(ctxt, self.group_snapshot,
test_snapshots)
class FCSanLookupService(object):
@ -5109,95 +5123,81 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
self.assertFalse(res)
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
'_get_consistencygroup_type',
return_value={"hypermetro": "true"})
def test_create_hypermetro_consistencygroup_success(self, mock_grouptype):
"""Test that create_consistencygroup return successfully."""
'_get_group_type',
return_value=[{"hypermetro": "true"}])
@cg_or_cg_snapshot
def test_create_hypermetro_group_success(self, mock_grouptype):
"""Test that create_group return successfully."""
ctxt = context.get_admin_context()
# Create consistency group
model_update = self.driver.create_consistencygroup(ctxt, self.cg)
# Create group
model_update = self.driver.create_group(ctxt, self.group)
self.assertEqual('available',
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'],
"Consistency Group created failed")
"Group created failed")
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
'_get_consistencygroup_type',
return_value={"hypermetro": "false"})
def test_create_normal_consistencygroup_success(self,
mock_grouptype):
"""Test that create_consistencygroup return successfully."""
'_get_group_type',
return_value=[{"hypermetro": "false"}])
@cg_or_cg_snapshot
def test_create_normal_group_success(self, mock_grouptype):
"""Test that create_group return successfully."""
ctxt = context.get_admin_context()
# Create consistency group
model_update = self.driver.create_consistencygroup(ctxt, self.cg)
# Create group
model_update = self.driver.create_group(ctxt, self.group)
self.assertEqual('available',
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update['status'],
"Consistency Group created failed")
"Group created failed")
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
'_get_consistencygroup_type',
return_value={"hypermetro": "true"})
def test_delete_hypermetro_consistencygroup_success(self, mock_grouptype):
"""Test that create_consistencygroup return successfully."""
'_get_group_type',
return_value=[{"hypermetro": "true"}])
@cg_or_cg_snapshot
def test_delete_hypermetro_group_success(self, mock_grouptype):
"""Test that delete_group return successfully."""
test_volumes = [self.volume]
ctxt = context.get_admin_context()
# Create consistency group
model, volumes = self.driver.delete_consistencygroup(ctxt,
self.cg,
test_volumes)
self.assertEqual('available',
# Delete group
model, volumes = self.driver.delete_group(ctxt, self.group,
test_volumes)
self.assertEqual(fields.GroupStatus.DELETED,
model['status'],
"Consistency Group created failed")
def test_delete_normal_consistencygroup_success(self):
ctxt = context.get_admin_context()
test_volumes = [self.volume]
self.mock_object(huawei_driver.HuaweiBaseDriver,
'_get_consistencygroup_type',
return_value={"hypermetro": "false"})
model, volumes = self.driver.delete_consistencygroup(ctxt,
self.cg,
test_volumes)
self.assertEqual('available',
model['status'],
"Consistency Group created failed")
"Group deleted failed")
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
'_get_consistencygroup_type',
return_value={"hypermetro": "true"})
'_get_group_type',
return_value=[{"hypermetro": "false"}])
@cg_or_cg_snapshot
def test_delete_normal_group_success(self, mock_grouptype):
"""Test that delete_group return successfully."""
ctxt = context.get_admin_context()
test_volumes = [self.volume]
# Delete group
model, volumes = self.driver.delete_group(ctxt, self.group,
test_volumes)
self.assertEqual(fields.GroupStatus.DELETED,
model['status'],
"Group deleted failed")
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
'_get_group_type',
return_value=[{"hypermetro": "true"}])
@mock.patch.object(huawei_driver.huawei_utils, 'get_volume_metadata',
return_value={'hypermetro_id': '3400a30d844d0007',
'remote_lun_id': '59'})
def test_update_consistencygroup_success(self,
mock_grouptype,
mock_metadata):
"""Test that create_consistencygroup return successfully."""
@cg_or_cg_snapshot
def test_update_group_success(self, mock_grouptype, mock_metadata):
"""Test that update_group return successfully."""
ctxt = context.get_admin_context()
add_volumes = [self.volume]
remove_volumes = [self.volume]
# Create consistency group
model_update = self.driver.update_consistencygroup(ctxt,
self.cg,
add_volumes,
remove_volumes)
self.assertEqual('available',
# Update group
model_update = self.driver.update_group(ctxt, self.group,
add_volumes, remove_volumes)
self.assertEqual(fields.GroupStatus.AVAILABLE,
model_update[0]['status'],
"Consistency Group update failed")
def test_create_hypermetro_consistencygroup_success_2(self):
ctxt = context.get_admin_context()
# Create consistency group
temp_cg = copy.deepcopy(self.cg)
temp_cg['volume_type_id'] = '550c089b-bfdd-4f7f-86e1-3ba88125555c,'
self.mock_object(volume_types, 'get_volume_type',
return_value=test_hypermetro_type)
model_update = self.driver.create_consistencygroup(ctxt, temp_cg)
self.assertEqual('available',
model_update['status'],
"Consistency Group created failed")
"Group update failed")
def test_is_initiator_associated_to_host_raise(self):
self.assertRaises(exception.VolumeBackendAPIException,

View File

@ -29,6 +29,7 @@ from cinder import context
from cinder import exception
from cinder.i18n import _
from cinder import interface
from cinder.objects import fields
from cinder import utils
from cinder.volume import driver
from cinder.volume.drivers.huawei import constants
@ -191,6 +192,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
pool['thick_provisioning_support'] = True
pool['thin_provisioning_support'] = True
pool['smarttier'] = True
pool['consistent_group_snapshot_enabled'] = True
if self.configuration.san_product == "Dorado":
pool['smarttier'] = False
@ -198,8 +200,6 @@ class HuaweiBaseDriver(driver.VolumeDriver):
if self.metro_flag:
pool['hypermetro'] = self.check_func_support("HyperMetroPair")
pool['consistencygroup_support'] = (
self.check_func_support("HyperMetro_ConsistentGroup"))
# Asign the support function to global paramenter.
self.support_func = pool
@ -224,17 +224,28 @@ class HuaweiBaseDriver(driver.VolumeDriver):
opts = self._get_volume_params_from_specs(specs)
return opts
def _get_consistencygroup_type(self, group):
specs = {}
opts = {}
type_id = group.volume_type_id.split(",")
if type_id[0] and len(type_id) == 2:
ctxt = context.get_admin_context()
volume_type = volume_types.get_volume_type(ctxt, type_id[0])
specs = dict(volume_type).get('extra_specs')
opts = self._get_volume_params_from_specs(specs)
def _get_group_type(self, group):
opts = []
vol_types = group.volume_types
for vol_type in vol_types:
specs = vol_type.extra_specs
opts.append(self._get_volume_params_from_specs(specs))
return opts
def _check_volume_type_support(self, opts, vol_type):
if not opts:
return False
support = True
for opt in opts:
if opt.get(vol_type) != 'true':
support = False
break
return support
def _get_volume_params_from_specs(self, specs):
"""Return the volume parameters from extra specs."""
opts_capability = {
@ -1599,24 +1610,26 @@ class HuaweiBaseDriver(driver.VolumeDriver):
self.client.is_host_associated_to_hostgroup(host_id)):
self.client.remove_host(host_id)
def create_consistencygroup(self, context, group):
"""Creates a consistencygroup."""
model_update = {'status': 'available'}
opts = self._get_consistencygroup_type(group)
if (opts.get('hypermetro') == 'true'):
@huawei_utils.check_whether_operate_consistency_group
def create_group(self, context, group):
"""Creates a group."""
model_update = {'status': fields.GroupStatus.AVAILABLE}
opts = self._get_group_type(group)
if self._check_volume_type_support(opts, 'hypermetro'):
metro = hypermetro.HuaweiHyperMetro(self.client,
self.rmt_client,
self.configuration)
metro.create_consistencygroup(group)
return model_update
# Array will create CG at create_cgsnapshot time. Cinder will
# maintain the CG and volumes relationship in the db.
# Array will create group at create_group_snapshot time. Cinder will
# maintain the group and volumes relationship in the db.
return model_update
def delete_consistencygroup(self, context, group, volumes):
opts = self._get_consistencygroup_type(group)
if opts.get('hypermetro') == 'true':
@huawei_utils.check_whether_operate_consistency_group
def delete_group(self, context, group, volumes):
opts = self._get_group_type(group)
if self._check_volume_type_support(opts, 'hypermetro'):
metro = hypermetro.HuaweiHyperMetro(self.client,
self.rmt_client,
self.configuration)
@ -1624,7 +1637,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
model_update = {}
volumes_model_update = []
model_update.update({'status': group.status})
model_update.update({'status': fields.GroupStatus.DELETED})
for volume_ref in volumes:
try:
@ -1637,12 +1650,12 @@ class HuaweiBaseDriver(driver.VolumeDriver):
return model_update, volumes_model_update
def update_consistencygroup(self, context, group,
add_volumes,
remove_volumes):
model_update = {'status': 'available'}
opts = self._get_consistencygroup_type(group)
if opts.get('hypermetro') == 'true':
@huawei_utils.check_whether_operate_consistency_group
def update_group(self, context, group,
add_volumes=None, remove_volumes=None):
model_update = {'status': fields.GroupStatus.AVAILABLE}
opts = self._get_group_type(group)
if self._check_volume_type_support(opts, 'hypermetro'):
metro = hypermetro.HuaweiHyperMetro(self.client,
self.rmt_client,
self.configuration)
@ -1651,15 +1664,23 @@ class HuaweiBaseDriver(driver.VolumeDriver):
remove_volumes)
return model_update, None, None
# Array will create CG at create_cgsnapshot time. Cinder will
# maintain the CG and volumes relationship in the db.
# Array will create group at create_group_snapshot time. Cinder will
# maintain the group and volumes relationship in the db.
return model_update, None, None
def create_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Create cgsnapshot."""
LOG.info('Create cgsnapshot for consistency group'
': %(group_id)s',
{'group_id': cgsnapshot.consistencygroup_id})
@huawei_utils.check_whether_operate_consistency_group
def create_group_from_src(self, context, group, volumes,
group_snapshot=None, snapshots=None,
source_group=None, source_vols=None):
err_msg = _("Huawei Storage doesn't support create_group_from_src.")
LOG.error(err_msg)
raise exception.VolumeBackendAPIException(data=err_msg)
@huawei_utils.check_whether_operate_consistency_group
def create_group_snapshot(self, context, group_snapshot, snapshots):
"""Create group snapshot."""
LOG.info('Create group snapshot for group'
': %(group_id)s', {'group_id': group_snapshot.group_id})
model_update = {}
snapshots_model_update = []
@ -1682,48 +1703,50 @@ class HuaweiBaseDriver(driver.VolumeDriver):
info = self.client.create_snapshot(lun_id,
snapshot_name,
snapshot_description)
snapshot_model_update = {'id': snapshot.id,
'status': 'available',
'provider_location': info['ID']}
snapshots_model_update.append(snapshot_model_update)
snap_model_update = {'id': snapshot.id,
'status': fields.SnapshotStatus.AVAILABLE,
'provider_location': info['ID']}
snapshots_model_update.append(snap_model_update)
added_snapshots_info.append(info)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Create cgsnapshots failed. "
"Cgsnapshot id: %s.", cgsnapshot.id)
LOG.error("Create group snapshots failed. "
"Group snapshot id: %s.", group_snapshot.id)
snapshot_ids = [added_snapshot['ID']
for added_snapshot in added_snapshots_info]
try:
self.client.activate_snapshot(snapshot_ids)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Active cgsnapshots failed. "
"Cgsnapshot id: %s.", cgsnapshot.id)
LOG.error("Active group snapshots failed. "
"Group snapshot id: %s.", group_snapshot.id)
model_update['status'] = 'available'
model_update['status'] = fields.GroupSnapshotStatus.AVAILABLE
return model_update, snapshots_model_update
def delete_cgsnapshot(self, context, cgsnapshot, snapshots):
"""Delete consistency group snapshot."""
LOG.info('Delete cgsnapshot %(snap_id)s for consistency group: '
@huawei_utils.check_whether_operate_consistency_group
def delete_group_snapshot(self, context, group_snapshot, snapshots):
"""Delete group snapshot."""
LOG.info('Delete group snapshot %(snap_id)s for group: '
'%(group_id)s',
{'snap_id': cgsnapshot.id,
'group_id': cgsnapshot.consistencygroup_id})
{'snap_id': group_snapshot.id,
'group_id': group_snapshot.group_id})
model_update = {}
snapshots_model_update = []
model_update['status'] = cgsnapshot.status
model_update['status'] = fields.GroupSnapshotStatus.DELETED
for snapshot in snapshots:
try:
self.delete_snapshot(snapshot)
snapshots_model_update.append({'id': snapshot.id,
'status': 'deleted'})
snapshot_model = {'id': snapshot.id,
'status': fields.SnapshotStatus.DELETED}
snapshots_model_update.append(snapshot_model)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Delete cg snapshots failed. "
"Cgsnapshot id: %s", cgsnapshot.id)
LOG.error("Delete group snapshot failed. "
"Group snapshot id: %s", group_snapshot.id)
return model_update, snapshots_model_update

View File

@ -24,6 +24,7 @@ from cinder import exception
from cinder.i18n import _
from cinder import objects
from cinder.volume.drivers.huawei import constants
from cinder.volume import utils
LOG = logging.getLogger(__name__)
@ -112,3 +113,14 @@ def get_snapshot_metadata_value(snapshot):
return {item['key']: item['value'] for item in metadata}
return {}
def check_whether_operate_consistency_group(func):
def wrapper(self, context, group, *args, **kwargs):
if not utils.is_group_a_cg_snapshot_type(group):
msg = _("%s, the group or group snapshot is not cg or "
"cg_snapshot") % func.__name__
LOG.debug(msg)
raise NotImplementedError(msg)
return func(self, context, group, *args, **kwargs)
return wrapper

View File

@ -18,6 +18,7 @@ from oslo_log import log as logging
from cinder import exception
from cinder.i18n import _
from cinder.objects import fields
from cinder.volume.drivers.huawei import constants
from cinder.volume.drivers.huawei import huawei_utils
@ -286,7 +287,7 @@ class HuaweiHyperMetro(object):
{'group': group.id})
model_update = {}
volumes_model_update = []
model_update['status'] = group.status
model_update['status'] = fields.GroupStatus.DELETED
metrogroup_id = self.check_consistencygroup_need_to_stop(group)
if metrogroup_id:
self.client.delete_metrogroup(metrogroup_id)
@ -304,8 +305,6 @@ class HuaweiHyperMetro(object):
LOG.info("Update Consistency Group: %(group)s. "
"This adds or removes volumes from a CG.",
{'group': group.id})
model_update = {}
model_update['status'] = group.status
metrogroup_id = self.check_consistencygroup_need_to_stop(group)
if metrogroup_id:
# Deal with add volumes to CG

View File

@ -0,0 +1,3 @@
---
features:
- Add CG capability to generic volume groups in Huawei driver.