Huawei driver supports create_group_from_src
Huawei driver doesn't implement create_group_from_src interface right now,
though Cinder framework provides a generic implementation, but it cannot
guarantee the group volume data consistency for Huawei driver, so Huawei
driver is supposed to provide this ability itself.
Change-Id: I73b227bae51997bebb9070cc48466f5e0b3699c3
Closes-Bug: #1763242
(cherry picked from commit cce4f7d355
)
This commit is contained in:
parent
06168da381
commit
71284352ac
|
@ -544,7 +544,8 @@ FAKE_CREATE_SNAPSHOT_INFO_RESPONSE = """
|
|||
},
|
||||
"data": {
|
||||
"ID": "11",
|
||||
"NAME": "YheUoRwbSX2BxN7"
|
||||
"NAME": "YheUoRwbSX2BxN7",
|
||||
"WWN": "fake-wwn"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -558,7 +559,8 @@ FAKE_GET_SNAPSHOT_INFO_RESPONSE = """
|
|||
},
|
||||
"data": {
|
||||
"ID": "11",
|
||||
"NAME": "YheUoRwbSX2BxN7"
|
||||
"NAME": "YheUoRwbSX2BxN7",
|
||||
"WWN": "fake-wwn"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@ -2068,15 +2070,6 @@ 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
|
||||
|
@ -2322,14 +2315,16 @@ class HuaweiTestBase(test.TestCase):
|
|||
@mock.patch.object(rest_client, 'RestClient')
|
||||
def test_create_snapshot_success(self, mock_client):
|
||||
lun_info = self.driver.create_snapshot(self.snapshot)
|
||||
self.assertDictEqual({"huawei_snapshot_id": "11"},
|
||||
json.loads(lun_info['provider_location']))
|
||||
self.assertDictEqual(
|
||||
{"huawei_snapshot_id": "11", "huawei_snapshot_wwn": "fake-wwn"},
|
||||
json.loads(lun_info['provider_location']))
|
||||
|
||||
self.snapshot.volume_id = ID
|
||||
self.snapshot.volume = self.volume
|
||||
lun_info = self.driver.create_snapshot(self.snapshot)
|
||||
self.assertDictEqual({"huawei_snapshot_id": "11"},
|
||||
json.loads(lun_info['provider_location']))
|
||||
self.assertDictEqual(
|
||||
{"huawei_snapshot_id": "11", "huawei_snapshot_wwn": "fake-wwn"},
|
||||
json.loads(lun_info['provider_location']))
|
||||
|
||||
@ddt.data('1', '', '0')
|
||||
def test_copy_volume(self, input_speed):
|
||||
|
@ -4537,53 +4532,53 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
|
|||
iqn = self.driver.client._get_tgt_iqn_from_rest(ip)
|
||||
self.assertIsNone(iqn)
|
||||
|
||||
@cg_or_cg_snapshot
|
||||
def test_create_group_snapshot(self):
|
||||
test_snapshots = [self.snapshot]
|
||||
ctxt = context.get_admin_context()
|
||||
model, snapshots = (
|
||||
self.driver.create_group_snapshot(ctxt, self.group_snapshot,
|
||||
test_snapshots))
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
model, snapshots = self.driver.create_group_snapshot(
|
||||
ctxt, self.group_snapshot, test_snapshots)
|
||||
|
||||
self.assertEqual('21ec7341-9256-497b-97d9-ef48edcf0635',
|
||||
snapshots[0]['id'])
|
||||
self.assertEqual('available', snapshots[0]['status'])
|
||||
self.assertDictEqual({'huawei_snapshot_id': '11'},
|
||||
json.loads(snapshots[0]['provider_location']))
|
||||
self.assertDictEqual(
|
||||
{'huawei_snapshot_id': '11', 'huawei_snapshot_wwn': 'fake-wwn'},
|
||||
json.loads(snapshots[0]['provider_location']))
|
||||
self.assertEqual(fields.GroupSnapshotStatus.AVAILABLE, model['status'])
|
||||
|
||||
@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.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
self.mock_object(
|
||||
rest_client.RestClient, 'create_snapshot',
|
||||
side_effect=exception.VolumeBackendAPIException(data='err'))
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.create_group_snapshot,
|
||||
ctxt,
|
||||
self.group_snapshot,
|
||||
test_snapshots)
|
||||
ctxt, self.group_snapshot, test_snapshots)
|
||||
|
||||
@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.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
self.mock_object(
|
||||
rest_client.RestClient, 'activate_snapshot',
|
||||
side_effect=exception.VolumeBackendAPIException(data='err'))
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
self.driver.create_group_snapshot,
|
||||
ctxt,
|
||||
self.group_snapshot,
|
||||
test_snapshots)
|
||||
ctxt, self.group_snapshot, test_snapshots)
|
||||
|
||||
@cg_or_cg_snapshot
|
||||
def test_delete_group_snapshot(self):
|
||||
test_snapshots = [self.snapshot]
|
||||
ctxt = context.get_admin_context()
|
||||
self.driver.delete_group_snapshot(ctxt, self.group_snapshot,
|
||||
test_snapshots)
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
self.driver.delete_group_snapshot(
|
||||
ctxt, self.group_snapshot, test_snapshots)
|
||||
|
||||
|
||||
class FCSanLookupService(object):
|
||||
|
@ -5386,82 +5381,79 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
|||
res = self.driver.client.is_host_associated_to_hostgroup('1')
|
||||
self.assertFalse(res)
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
|
||||
'_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 group
|
||||
model_update = self.driver.create_group(ctxt, self.group)
|
||||
@ddt.data([{"hypermetro": "true"}], [])
|
||||
def test_create_group_success(self, cg_type):
|
||||
self.mock_object(huawei_driver.HuaweiBaseDriver, '_get_group_type',
|
||||
return_value=cg_type)
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
model_update = self.driver.create_group(None, self.group)
|
||||
self.assertEqual(fields.GroupStatus.AVAILABLE, model_update['status'])
|
||||
|
||||
self.assertEqual(fields.GroupStatus.AVAILABLE,
|
||||
model_update['status'],
|
||||
"Group created failed")
|
||||
@ddt.data(
|
||||
([fake_snapshot.fake_snapshot_obj(
|
||||
None, provider_location=SNAP_PROVIDER_LOCATION, id=ID)],
|
||||
[], False),
|
||||
([], [fake_volume.fake_volume_obj(
|
||||
None, provider_location=PROVIDER_LOCATION, id=ID)], True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_create_group_from_src(self, snapshots, source_vols, tmp_snap):
|
||||
self.mock_object(huawei_driver.HuaweiBaseDriver, '_get_group_type',
|
||||
return_value=[])
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
|
||||
'_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 group
|
||||
model_update = self.driver.create_group(ctxt, self.group)
|
||||
create_snap_mock = self.mock_object(
|
||||
self.driver, '_create_group_snapshot',
|
||||
wraps=self.driver._create_group_snapshot)
|
||||
delete_snap_mock = self.mock_object(
|
||||
self.driver, '_delete_group_snapshot',
|
||||
wraps=self.driver._delete_group_snapshot)
|
||||
|
||||
self.assertEqual(fields.GroupStatus.AVAILABLE,
|
||||
model_update['status'],
|
||||
"Group created failed")
|
||||
model_update, volumes_model_update = self.driver.create_group_from_src(
|
||||
None, self.group, [self.volume], snapshots=snapshots,
|
||||
source_vols=source_vols)
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
|
||||
'_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."""
|
||||
if tmp_snap:
|
||||
create_snap_mock.assert_called_once()
|
||||
delete_snap_mock.assert_called_once()
|
||||
else:
|
||||
create_snap_mock.assert_not_called()
|
||||
delete_snap_mock.assert_not_called()
|
||||
|
||||
self.assertDictEqual({'status': fields.GroupStatus.AVAILABLE},
|
||||
model_update)
|
||||
self.assertEqual(1, len(volumes_model_update))
|
||||
self.assertEqual(ID, volumes_model_update[0]['id'])
|
||||
|
||||
@ddt.data([{"hypermetro": "true"}], [])
|
||||
def test_delete_group_success(self, cg_type):
|
||||
test_volumes = [self.volume]
|
||||
ctxt = context.get_admin_context()
|
||||
# Delete group
|
||||
model, volumes = self.driver.delete_group(ctxt, self.group,
|
||||
test_volumes)
|
||||
self.assertEqual(fields.GroupStatus.DELETED,
|
||||
model['status'],
|
||||
"Group deleted failed")
|
||||
self.mock_object(huawei_driver.HuaweiBaseDriver, '_get_group_type',
|
||||
return_value=cg_type)
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
model, volumes = self.driver.delete_group(
|
||||
ctxt, self.group, test_volumes)
|
||||
self.assertEqual(fields.GroupStatus.DELETED, model['status'])
|
||||
|
||||
@mock.patch.object(huawei_driver.HuaweiBaseDriver,
|
||||
'_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',
|
||||
@mock.patch.object(huawei_driver.HuaweiBaseDriver, '_get_group_type',
|
||||
return_value=[{"hypermetro": "true"}])
|
||||
@mock.patch.object(huawei_driver.huawei_utils, 'get_lun_metadata',
|
||||
return_value={'hypermetro_id': '3400a30d844d0007',
|
||||
'remote_lun_id': '59'})
|
||||
@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]
|
||||
# Update group
|
||||
model_update = self.driver.update_group(ctxt, self.group,
|
||||
add_volumes, remove_volumes)
|
||||
self.mock_object(volume_utils, 'is_group_a_cg_snapshot_type',
|
||||
return_value=True)
|
||||
model_update = self.driver.update_group(
|
||||
ctxt, self.group, add_volumes, remove_volumes)
|
||||
self.assertEqual(fields.GroupStatus.AVAILABLE,
|
||||
model_update[0]['status'],
|
||||
"Group update failed")
|
||||
model_update[0]['status'])
|
||||
|
||||
def test_is_initiator_associated_to_host_raise(self):
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
|
|
|
@ -31,6 +31,7 @@ from cinder import coordination
|
|||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import interface
|
||||
from cinder import objects
|
||||
from cinder.objects import fields
|
||||
from cinder.volume import configuration
|
||||
from cinder.volume import driver
|
||||
|
@ -194,6 +195,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
pool['thick_provisioning_support'] = True
|
||||
pool['thin_provisioning_support'] = True
|
||||
pool['smarttier'] = True
|
||||
pool['consistencygroup_support'] = True
|
||||
pool['consistent_group_snapshot_enabled'] = True
|
||||
|
||||
if self.configuration.san_product == "Dorado":
|
||||
|
@ -226,28 +228,6 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
opts = self._get_volume_params_from_specs(specs)
|
||||
return opts
|
||||
|
||||
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 = {
|
||||
|
@ -687,7 +667,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
self.configuration.lun_policy)
|
||||
|
||||
lun_params = {
|
||||
'NAME': dst_volume_name,
|
||||
'NAME': huawei_utils.encode_name(dst_volume_name),
|
||||
'PARENTID': pool_info['ID'],
|
||||
'DESCRIPTION': lun_info['DESCRIPTION'],
|
||||
'ALLOCTYPE': opts.get('LUNType', lun_info['ALLOCTYPE']),
|
||||
|
@ -887,7 +867,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
|
||||
self.client.extend_lun(lun_id, new_size)
|
||||
|
||||
def create_snapshot(self, snapshot):
|
||||
def _create_snapshot_base(self, snapshot):
|
||||
volume = snapshot.volume
|
||||
if not volume:
|
||||
msg = _("Can't get volume id from snapshot, snapshot: %(id)s"
|
||||
|
@ -902,11 +882,23 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
snapshot_name,
|
||||
snapshot_description)
|
||||
snapshot_id = snapshot_info['ID']
|
||||
self.client.activate_snapshot(snapshot_id)
|
||||
return snapshot_id
|
||||
|
||||
location = huawei_utils.to_string(huawei_snapshot_id=snapshot_id)
|
||||
return {'provider_location': location,
|
||||
'lun_info': snapshot_info}
|
||||
def create_snapshot(self, snapshot):
|
||||
snapshot_id = self._create_snapshot_base(snapshot)
|
||||
try:
|
||||
self.client.activate_snapshot(snapshot_id)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Active snapshot %s failed, now deleting it.",
|
||||
snapshot_id)
|
||||
self.client.delete_snapshot(snapshot_id)
|
||||
|
||||
snapshot_info = self.client.get_snapshot_info(snapshot_id)
|
||||
location = huawei_utils.to_string(
|
||||
huawei_snapshot_id=snapshot_id,
|
||||
huawei_snapshot_wwn=snapshot_info['WWN'])
|
||||
return {'provider_location': location}
|
||||
|
||||
def delete_snapshot(self, snapshot):
|
||||
LOG.info('Delete snapshot %s.', snapshot.id)
|
||||
|
@ -1592,52 +1584,135 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
self.client.is_host_associated_to_hostgroup(host_id)):
|
||||
self.client.remove_host(host_id)
|
||||
|
||||
@huawei_utils.check_whether_operate_consistency_group
|
||||
def _get_group_type(self, group):
|
||||
opts = []
|
||||
for vol_type in group.volume_types:
|
||||
specs = vol_type.extra_specs
|
||||
opts.append(self._get_volume_params_from_specs(specs))
|
||||
|
||||
return opts
|
||||
|
||||
def _check_group_type_support(self, opts, vol_type):
|
||||
if not opts:
|
||||
return False
|
||||
|
||||
for opt in opts:
|
||||
if opt.get(vol_type) == 'true':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_group_type_value(self, opts, vol_type):
|
||||
if not opts:
|
||||
return
|
||||
|
||||
for opt in opts:
|
||||
if vol_type in opt:
|
||||
return opt[vol_type]
|
||||
|
||||
def create_group(self, context, group):
|
||||
"""Creates a group."""
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||
opts = self._get_group_type(group)
|
||||
if self._check_volume_type_support(opts, 'hypermetro'):
|
||||
|
||||
if self._check_group_type_support(opts, 'hypermetro'):
|
||||
if not self.check_func_support("HyperMetro_ConsistentGroup"):
|
||||
msg = _("Can't create consistency group, array does not "
|
||||
"support hypermetro consistentgroup, "
|
||||
"group id: %(group_id)s."
|
||||
) % {"group_id": group.id}
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
metro = hypermetro.HuaweiHyperMetro(self.client,
|
||||
self.rmt_client,
|
||||
self.configuration)
|
||||
metro.create_consistencygroup(group)
|
||||
return model_update
|
||||
|
||||
# Array will create group at create_group_snapshot time. Cinder will
|
||||
# maintain the group and volumes relationship in the db.
|
||||
return model_update
|
||||
|
||||
@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)
|
||||
return metro.delete_consistencygroup(context, group, volumes)
|
||||
def create_group_from_src(self, context, group, volumes,
|
||||
group_snapshot=None, snapshots=None,
|
||||
source_group=None, source_vols=None):
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {}
|
||||
model_update = self.create_group(context, group)
|
||||
volumes_model_update = []
|
||||
model_update.update({'status': fields.GroupStatus.DELETED})
|
||||
delete_snapshots = False
|
||||
|
||||
for volume_ref in volumes:
|
||||
try:
|
||||
self.delete_volume(volume_ref)
|
||||
volumes_model_update.append(
|
||||
{'id': volume_ref.id, 'status': 'deleted'})
|
||||
except Exception:
|
||||
volumes_model_update.append(
|
||||
{'id': volume_ref.id, 'status': 'error_deleting'})
|
||||
if not snapshots and source_vols:
|
||||
snapshots = []
|
||||
for src_vol in source_vols:
|
||||
vol_kwargs = {
|
||||
'id': src_vol.id,
|
||||
'provider_location': src_vol.provider_location,
|
||||
}
|
||||
snapshot_kwargs = {'id': six.text_type(uuid.uuid4()),
|
||||
'volume': objects.Volume(**vol_kwargs)}
|
||||
snapshot = objects.Snapshot(**snapshot_kwargs)
|
||||
snapshots.append(snapshot)
|
||||
|
||||
snapshots_model_update = self._create_group_snapshot(snapshots)
|
||||
for i, model in enumerate(snapshots_model_update):
|
||||
snapshot = snapshots[i]
|
||||
snapshot.provider_location = model['provider_location']
|
||||
|
||||
delete_snapshots = True
|
||||
|
||||
if snapshots:
|
||||
for i, vol in enumerate(volumes):
|
||||
snapshot = snapshots[i]
|
||||
vol_model_update = self.create_volume_from_snapshot(
|
||||
vol, snapshot)
|
||||
vol_model_update.update({'id': vol.id})
|
||||
volumes_model_update.append(vol_model_update)
|
||||
|
||||
if delete_snapshots:
|
||||
self._delete_group_snapshot(snapshots)
|
||||
|
||||
return model_update, volumes_model_update
|
||||
|
||||
def delete_group(self, context, group, volumes):
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
opts = self._get_group_type(group)
|
||||
model_update = {'status': fields.GroupStatus.DELETED}
|
||||
volumes_model_update = []
|
||||
|
||||
if self._check_group_type_support(opts, 'hypermetro'):
|
||||
metro = hypermetro.HuaweiHyperMetro(self.client,
|
||||
self.rmt_client,
|
||||
self.configuration)
|
||||
metro.delete_consistencygroup(context, group, volumes)
|
||||
|
||||
for volume in volumes:
|
||||
volume_model_update = {'id': volume.id}
|
||||
try:
|
||||
self.delete_volume(volume)
|
||||
except Exception:
|
||||
LOG.exception('Delete volume %s failed.', volume)
|
||||
volume_model_update.update({'status': 'error_deleting'})
|
||||
else:
|
||||
volume_model_update.update({'status': 'deleted'})
|
||||
|
||||
volumes_model_update.append(volume_model_update)
|
||||
|
||||
return model_update, volumes_model_update
|
||||
|
||||
@huawei_utils.check_whether_operate_consistency_group
|
||||
def update_group(self, context, group,
|
||||
add_volumes=None, remove_volumes=None):
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {'status': fields.GroupStatus.AVAILABLE}
|
||||
opts = self._get_group_type(group)
|
||||
if self._check_volume_type_support(opts, 'hypermetro'):
|
||||
if self._check_group_type_support(opts, 'hypermetro'):
|
||||
metro = hypermetro.HuaweiHyperMetro(self.client,
|
||||
self.rmt_client,
|
||||
self.configuration)
|
||||
|
@ -1646,93 +1721,91 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
|||
remove_volumes)
|
||||
return model_update, None, None
|
||||
|
||||
# Array will create group at create_group_snapshot time. Cinder will
|
||||
# maintain the group and volumes relationship in the db.
|
||||
for volume in add_volumes:
|
||||
self._check_volume_exist_on_array(
|
||||
volume, constants.VOLUME_NOT_EXISTS_RAISE)
|
||||
|
||||
return model_update, None, None
|
||||
|
||||
@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})
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
|
||||
raise NotImplementedError()
|
||||
|
||||
model_update = {}
|
||||
LOG.info('Create group snapshot for group: %(group_id)s',
|
||||
{'group_id': group_snapshot.group_id})
|
||||
|
||||
snapshots_model_update = self._create_group_snapshot(snapshots)
|
||||
model_update = {'status': fields.GroupSnapshotStatus.AVAILABLE}
|
||||
return model_update, snapshots_model_update
|
||||
|
||||
def _create_group_snapshot(self, snapshots):
|
||||
snapshots_model_update = []
|
||||
added_snapshots_info = []
|
||||
|
||||
try:
|
||||
for snapshot in snapshots:
|
||||
volume = snapshot.volume
|
||||
if not volume:
|
||||
msg = _("Can't get volume id from snapshot, "
|
||||
"snapshot: %(id)s") % {'id': snapshot.id}
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
lun_id, lun_wwn = huawei_utils.get_volume_lun_id(
|
||||
self.client, volume)
|
||||
snapshot_name = huawei_utils.encode_name(snapshot.id)
|
||||
snapshot_description = snapshot.id
|
||||
info = self.client.create_snapshot(lun_id,
|
||||
snapshot_name,
|
||||
snapshot_description)
|
||||
snapshot_id = self._create_snapshot_base(snapshot)
|
||||
info = self.client.get_snapshot_info(snapshot_id)
|
||||
location = huawei_utils.to_string(
|
||||
huawei_snapshot_id=info['ID'])
|
||||
snap_model_update = {'id': snapshot.id,
|
||||
'status': fields.SnapshotStatus.AVAILABLE,
|
||||
'provider_location': location}
|
||||
snapshots_model_update.append(snap_model_update)
|
||||
huawei_snapshot_id=info['ID'],
|
||||
huawei_snapshot_wwn=info['WWN'])
|
||||
snapshot_model_update = {
|
||||
'id': snapshot.id,
|
||||
'status': fields.SnapshotStatus.AVAILABLE,
|
||||
'provider_location': location,
|
||||
}
|
||||
snapshots_model_update.append(snapshot_model_update)
|
||||
added_snapshots_info.append(info)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Create group snapshots failed. "
|
||||
"Group snapshot id: %s.", group_snapshot.id)
|
||||
for added_snapshot in added_snapshots_info:
|
||||
self.client.delete_snapshot(added_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 group snapshots failed. "
|
||||
"Group snapshot id: %s.", group_snapshot.id)
|
||||
LOG.error("Active group snapshots %s failed.", snapshot_ids)
|
||||
for snapshot_id in snapshot_ids:
|
||||
self.client.delete_snapshot(snapshot_id)
|
||||
|
||||
model_update['status'] = fields.GroupSnapshotStatus.AVAILABLE
|
||||
return snapshots_model_update
|
||||
|
||||
return model_update, snapshots_model_update
|
||||
|
||||
@huawei_utils.check_whether_operate_consistency_group
|
||||
def delete_group_snapshot(self, context, group_snapshot, snapshots):
|
||||
"""Delete group snapshot."""
|
||||
if not volume_utils.is_group_a_cg_snapshot_type(group_snapshot):
|
||||
raise NotImplementedError()
|
||||
|
||||
LOG.info('Delete group snapshot %(snap_id)s for group: '
|
||||
'%(group_id)s',
|
||||
{'snap_id': group_snapshot.id,
|
||||
'group_id': group_snapshot.group_id})
|
||||
|
||||
model_update = {}
|
||||
snapshots_model_update = []
|
||||
model_update['status'] = fields.GroupSnapshotStatus.DELETED
|
||||
|
||||
for snapshot in snapshots:
|
||||
try:
|
||||
self.delete_snapshot(snapshot)
|
||||
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 group snapshot failed. "
|
||||
"Group snapshot id: %s", group_snapshot.id)
|
||||
try:
|
||||
snapshots_model_update = self._delete_group_snapshot(snapshots)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Delete group snapshots failed. "
|
||||
"Group snapshot id: %s", group_snapshot.id)
|
||||
|
||||
model_update = {'status': fields.GroupSnapshotStatus.DELETED}
|
||||
return model_update, snapshots_model_update
|
||||
|
||||
def _delete_group_snapshot(self, snapshots):
|
||||
snapshots_model_update = []
|
||||
for snapshot in snapshots:
|
||||
self.delete_snapshot(snapshot)
|
||||
snapshot_model_update = {
|
||||
'id': snapshot.id,
|
||||
'status': fields.SnapshotStatus.DELETED
|
||||
}
|
||||
snapshots_model_update.append(snapshot_model_update)
|
||||
|
||||
return snapshots_model_update
|
||||
|
||||
def _classify_volume(self, volumes):
|
||||
normal_volumes = []
|
||||
replica_volumes = []
|
||||
|
|
Loading…
Reference in New Issue