fix 'huawei dorado v6' create new volume from snapshot error
change the luncopy mode,in dorado v6 we use clone_pair modified some interface to call dorado v6 Closes-Bug: #1942342 Change-Id: If26ebd6a94512b1d8ecc1b786f518f667fc19ffe
This commit is contained in:
parent
97aeffa9f5
commit
77da18785a
@ -2087,6 +2087,61 @@ MAP_COMMAND_TO_FAKE_RESPONSE['/mappingview/associate/portgroup?TYPE=245&ASSOC'
|
||||
'IATEOBJTYPE=257&ASSOCIATEOBJID=1114113/GET'] = (
|
||||
FAKE_COMMON_SUCCESS_RESPONSE)
|
||||
|
||||
FAKE_CLONEPAIR_PAIRID_GET_RESPONSE = """
|
||||
{
|
||||
"data":{
|
||||
"name": "fake_clonepair_name",
|
||||
"copyStatus": "0",
|
||||
"syncStatus": "2"
|
||||
},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/clonepair/fake_clonepair_id/GET'] = (
|
||||
FAKE_CLONEPAIR_PAIRID_GET_RESPONSE)
|
||||
|
||||
FAKE_CLONEPAIR_PAIRID_DELETE_RESPONSE = """
|
||||
{
|
||||
"data":{
|
||||
"name": "fake_clonepair_name"
|
||||
},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": "0"
|
||||
}
|
||||
}
|
||||
"""
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/clonepair/fake_clonepair_id/DELETE'] = (
|
||||
FAKE_CLONEPAIR_PAIRID_DELETE_RESPONSE)
|
||||
|
||||
FAKE_CLONEPAIR_RELATION_RESPONSE = """
|
||||
{
|
||||
"data":{
|
||||
"ID": "fake_clonepair_id"
|
||||
},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
"""
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/clonepair/relation/POST'] = (
|
||||
FAKE_CLONEPAIR_RELATION_RESPONSE)
|
||||
|
||||
FAKE_CLONEPAIR_SYNCHRONIZE_RESPONSE = """
|
||||
{
|
||||
"data":{},
|
||||
"error": {
|
||||
"code": 0,
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
"""
|
||||
MAP_COMMAND_TO_FAKE_RESPONSE['/clonepair/synchronize/PUT'] = (
|
||||
FAKE_CLONEPAIR_SYNCHRONIZE_RESPONSE)
|
||||
|
||||
REPLICA_BACKEND_ID = 'huawei-replica-1'
|
||||
|
||||
@ -2235,6 +2290,7 @@ class FakeISCSIStorage(huawei_driver.HuaweiISCSIDriver):
|
||||
self.active_backend_id = None
|
||||
self.replica = None
|
||||
self.support_func = None
|
||||
self.is_dorado_v6 = False
|
||||
|
||||
def do_setup(self):
|
||||
self.metro_flag = True
|
||||
@ -2262,6 +2318,7 @@ class FakeFCStorage(huawei_driver.HuaweiFCDriver):
|
||||
self.active_backend_id = None
|
||||
self.replica = None
|
||||
self.support_func = None
|
||||
self.is_dorado_v6 = False
|
||||
|
||||
def do_setup(self):
|
||||
self.metro_flag = True
|
||||
@ -2839,12 +2896,18 @@ class HuaweiISCSIDriverTestCase(HuaweiTestBase):
|
||||
def test_delete_snapshot_success(self):
|
||||
self.driver.delete_snapshot(self.snapshot)
|
||||
|
||||
def test_create_volume_from_snapsuccess(self):
|
||||
@ddt.data(True, False)
|
||||
def test_create_volume_from_snapsuccess(self, is_dorado_v6):
|
||||
self.mock_object(
|
||||
huawei_utils,
|
||||
'get_volume_params',
|
||||
return_value={'replication_enabled': True})
|
||||
self.mock_object(replication.ReplicaCommonDriver, 'sync')
|
||||
self.mock_object(self.driver.client, 'get_snapshot_info_by_name',
|
||||
return_value=
|
||||
{'ID': ID, 'RUNNINGSTATUS': constants.STATUS_ACTIVE})
|
||||
self.configuration.lun_copy_speed = 2
|
||||
self.driver.is_dorado_v6 = is_dorado_v6
|
||||
model_update = self.driver.create_volume_from_snapshot(self.volume,
|
||||
self.snapshot)
|
||||
expect_value = {"huawei_lun_id": "1",
|
||||
@ -4619,7 +4682,13 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
def test_delete_snapshot_success(self):
|
||||
self.driver.delete_snapshot(self.snapshot)
|
||||
|
||||
def test_create_volume_from_snapsuccess(self):
|
||||
@ddt.data(True, False)
|
||||
def test_create_volume_from_snapsuccess(self, is_dorado_v6):
|
||||
self.configuration.lun_copy_speed = 2
|
||||
self.driver.is_dorado_v6 = is_dorado_v6
|
||||
self.mock_object(self.driver.client, 'get_snapshot_info_by_name',
|
||||
return_value=
|
||||
{'ID': ID, 'RUNNINGSTATUS': constants.STATUS_ACTIVE})
|
||||
lun_info = self.driver.create_volume_from_snapshot(self.volume,
|
||||
self.snapshot)
|
||||
expect_value = {"huawei_lun_id": "1",
|
||||
@ -5353,10 +5422,10 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
|
||||
@ddt.data(
|
||||
([fake_snapshot.fake_snapshot_obj(
|
||||
None, provider_location=SNAP_PROVIDER_LOCATION, id=ID)],
|
||||
[], False),
|
||||
None, provider_location=SNAP_PROVIDER_LOCATION, id=ID,
|
||||
volume_size=1)], [], False),
|
||||
([], [fake_volume.fake_volume_obj(
|
||||
None, provider_location=PROVIDER_LOCATION, id=ID)], True),
|
||||
None, provider_location=PROVIDER_LOCATION, id=ID, size=1)], True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_create_group_from_src(self, snapshots, source_vols, tmp_snap):
|
||||
@ -5372,7 +5441,8 @@ class HuaweiFCDriverTestCase(HuaweiTestBase):
|
||||
self.driver, '_delete_group_snapshot',
|
||||
wraps=self.driver._delete_group_snapshot)
|
||||
self.mock_object(self.driver.client, 'get_snapshot_info_by_name',
|
||||
return_value={'ID': ID})
|
||||
return_value=
|
||||
{'ID': ID, 'RUNNINGSTATUS': constants.STATUS_ACTIVE})
|
||||
|
||||
model_update, volumes_model_update = self.driver.create_group_from_src(
|
||||
None, self.group, [self.volume], snapshots=snapshots,
|
||||
|
@ -71,7 +71,7 @@ huawei_opts = [
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(huawei_opts, group=configuration.SHARED_CONF_GROUP)
|
||||
|
||||
snap_attrs = ('id', 'volume_id', 'volume', 'provider_location')
|
||||
snap_attrs = ('id', 'volume_id', 'volume', 'provider_location', 'volume_size')
|
||||
Snapshot = collections.namedtuple('Snapshot', snap_attrs)
|
||||
vol_attrs = ('id', 'lun_type', 'provider_location', 'metadata')
|
||||
Volume = collections.namedtuple('Volume', vol_attrs)
|
||||
@ -96,6 +96,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
self.support_func = None
|
||||
self.metro_flag = False
|
||||
self.replica = None
|
||||
self.is_dorado_v6 = False
|
||||
|
||||
@staticmethod
|
||||
def get_driver_options():
|
||||
@ -144,6 +145,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
self.client = rest_client.RestClient(self.configuration,
|
||||
**client_conf)
|
||||
self.client.login()
|
||||
self.is_dorado_v6 = huawei_utils.is_support_clone_pair(self.client)
|
||||
|
||||
# init remote client
|
||||
metro_san_address = self.configuration.safe_get("metro_san_address")
|
||||
@ -225,8 +227,9 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
|
||||
return volume_type
|
||||
|
||||
def _get_lun_params(self, volume, opts):
|
||||
def _get_lun_params(self, volume, opts, src_size=None):
|
||||
pool_name = volume_utils.extract_host(volume.host, level='pool')
|
||||
|
||||
params = {
|
||||
'TYPE': '11',
|
||||
'NAME': huawei_utils.encode_name(volume.id),
|
||||
@ -234,7 +237,8 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
'PARENTID': self.client.get_pool_id(pool_name),
|
||||
'DESCRIPTION': volume.name,
|
||||
'ALLOCTYPE': opts.get('LUNType', self.configuration.lun_type),
|
||||
'CAPACITY': int(volume.size) * constants.CAPACITY_UNIT,
|
||||
'CAPACITY': int(int(src_size) * constants.CAPACITY_UNIT if src_size
|
||||
else int(volume.size) * constants.CAPACITY_UNIT),
|
||||
'READCACHEPOLICY': self.configuration.lun_read_cache_policy,
|
||||
'WRITECACHEPOLICY': self.configuration.lun_write_cache_policy,
|
||||
}
|
||||
@ -261,12 +265,15 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
|
||||
return lun_info, model_update
|
||||
|
||||
def _create_base_type_volume(self, opts, volume):
|
||||
def _create_base_type_volume(self, opts, volume, src_size=None):
|
||||
"""Create volume and add some base type.
|
||||
|
||||
Base type is the service type which doesn't conflict with the other.
|
||||
"""
|
||||
lun_params = self._get_lun_params(volume, opts)
|
||||
if self.is_dorado_v6:
|
||||
lun_params = self._get_lun_params(volume, opts, src_size)
|
||||
else:
|
||||
lun_params = self._get_lun_params(volume, opts)
|
||||
lun_info, model_update = self._create_volume(lun_params)
|
||||
lun_id = lun_info['ID']
|
||||
|
||||
@ -623,30 +630,10 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
|
||||
return moved, {}
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a volume from a snapshot.
|
||||
|
||||
We use LUNcopy to copy a new volume from snapshot.
|
||||
The time needed increases as volume size does.
|
||||
"""
|
||||
opts = huawei_utils.get_volume_params(volume)
|
||||
if opts.get('hypermetro') and opts.get('replication_enabled'):
|
||||
msg = _("Hypermetro and Replication can not be "
|
||||
"used in the same volume_type.")
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
snapshot_info = huawei_utils.get_snapshot_info(self.client, snapshot)
|
||||
if not snapshot_info:
|
||||
msg = _('create_volume_from_snapshot: Snapshot %(name)s '
|
||||
'does not exist.') % {'name': snapshot.id}
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
snapshot_id = snapshot_info['ID']
|
||||
|
||||
lun_params, lun_info, model_update = self._create_base_type_volume(
|
||||
opts, volume)
|
||||
def _create_volume_wait_ready(self, opts, volume, snapshot_id,
|
||||
src_size=None):
|
||||
lun_params, lun_info, model_update = \
|
||||
self._create_base_type_volume(opts, volume, src_size)
|
||||
|
||||
tgt_lun_id = lun_info['ID']
|
||||
luncopy_name = huawei_utils.encode_name(volume.id)
|
||||
@ -670,12 +657,86 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
huawei_utils.wait_for_condition(_volume_ready,
|
||||
wait_interval,
|
||||
wait_interval * 10)
|
||||
return lun_params, lun_info, model_update
|
||||
|
||||
self._copy_volume(volume, luncopy_name,
|
||||
snapshot_id, tgt_lun_id)
|
||||
def _create_clone_pair(self, source_id, target_id, clone_speed):
|
||||
clone_pair_id = self.client.create_clone_pair(
|
||||
source_id, target_id, clone_speed)
|
||||
|
||||
def _pair_sync_completed():
|
||||
clone_pair_info = self.client.get_clone_pair_info(clone_pair_id)
|
||||
if clone_pair_info['copyStatus'] != constants.CLONE_STATUS_HEALTH:
|
||||
msg = _("ClonePair %s is abnormal.") % clone_pair_id
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
return (clone_pair_info['syncStatus'] in
|
||||
constants.CLONE_STATUS_COMPLETE)
|
||||
|
||||
self.client.sync_clone_pair(clone_pair_id)
|
||||
huawei_utils.wait_for_condition(
|
||||
_pair_sync_completed, self.configuration.lun_copy_wait_interval,
|
||||
self.configuration.lun_timeout)
|
||||
self.client.delete_clone_pair(clone_pair_id)
|
||||
|
||||
def _create_volume_from_snapshot(self, volume, snapshot, opts,
|
||||
clone_pair_flag=None):
|
||||
snapshot_info = huawei_utils.get_snapshot_info(self.client, snapshot)
|
||||
if not snapshot_info:
|
||||
msg = _('create_volume_from_snapshot: Snapshot %(name)s '
|
||||
'does not exist.') % {'name': snapshot.id}
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
snapshot_id = snapshot_info['ID']
|
||||
if snapshot_info.get("RUNNINGSTATUS") != constants.STATUS_ACTIVE:
|
||||
msg = _("Failed to create volume from snapshot duw to"
|
||||
"snapshot %s is not activate.") % snapshot_id
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
expect_size = int(int(volume.size) * constants.CAPACITY_UNIT)
|
||||
lun_params, lun_info, model_update = \
|
||||
self._create_volume_wait_ready(opts, volume, snapshot_id,
|
||||
src_size=snapshot.volume_size)
|
||||
tgt_lun_id = lun_info['ID']
|
||||
luncopy_name = huawei_utils.encode_name(volume.id)
|
||||
|
||||
if clone_pair_flag:
|
||||
clone_speed = self.configuration.lun_copy_speed
|
||||
self._create_clone_pair(snapshot_id, tgt_lun_id, clone_speed)
|
||||
else:
|
||||
self._copy_volume(volume, luncopy_name,
|
||||
snapshot_id, tgt_lun_id)
|
||||
try:
|
||||
if int(lun_info['CAPACITY']) < expect_size:
|
||||
self.client.extend_lun(lun_info["ID"], expect_size)
|
||||
lun_info = self.client.get_lun_info(lun_info["ID"])
|
||||
lun_params.update({"CAPACITY": expect_size})
|
||||
except Exception as err:
|
||||
LOG.exception('Extend lun %(lun_id)s error. Reason is %(err)s',
|
||||
{"lun_id": lun_info['ID'], "err": err})
|
||||
self._delete_lun_with_check(lun_info['ID'])
|
||||
raise
|
||||
|
||||
return lun_params, lun_info, model_update
|
||||
|
||||
def create_volume_from_snapshot(self, volume, snapshot):
|
||||
"""Create a volume from a snapshot.
|
||||
|
||||
We use LUNcopy to copy a new volume from snapshot.
|
||||
The time needed increases as volume size does.
|
||||
For Dorado V6 we use clone_pair
|
||||
"""
|
||||
opts = huawei_utils.get_volume_params(volume)
|
||||
if opts.get('hypermetro') and opts.get('replication_enabled'):
|
||||
msg = _("Hypermetro and Replication can not be "
|
||||
"used in the same volume_type.")
|
||||
LOG.error(msg)
|
||||
raise exception.VolumeBackendAPIException(data=msg)
|
||||
|
||||
lun_params, lun_info, model_update = \
|
||||
self._create_volume_from_snapshot(volume, snapshot, opts,
|
||||
self.is_dorado_v6)
|
||||
|
||||
# NOTE(jlc): Actually, we just only support replication here right
|
||||
# now, not hypermetro.
|
||||
model_update = self._add_extend_type_to_volume(opts, lun_params,
|
||||
lun_info, model_update)
|
||||
model_update['provider_location'] = huawei_utils.to_string(
|
||||
@ -692,6 +753,7 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
snapshot = Snapshot(id=uuid.uuid4().__str__(),
|
||||
volume_id=src_vref.id,
|
||||
volume=src_vref,
|
||||
volume_size=src_vref.size,
|
||||
provider_location=None)
|
||||
|
||||
# Create snapshot.
|
||||
@ -1556,7 +1618,8 @@ class HuaweiBaseDriver(driver.VolumeDriver):
|
||||
'provider_location': src_vol.provider_location,
|
||||
}
|
||||
snapshot_kwargs = {'id': six.text_type(uuid.uuid4()),
|
||||
'volume': objects.Volume(**vol_kwargs)}
|
||||
'volume': objects.Volume(**vol_kwargs),
|
||||
'volume_size': src_vol.size}
|
||||
snapshot = objects.Snapshot(**snapshot_kwargs)
|
||||
snapshots.append(snapshot)
|
||||
|
||||
|
@ -133,3 +133,8 @@ LUN_COPY_SPEED_TYPES = (
|
||||
) = ('1', '2', '3', '4')
|
||||
|
||||
MAX_QUERY_COUNT = 100
|
||||
|
||||
CLONE_STATUS_HEALTH = '0'
|
||||
CLONE_STATUS_COMPLETE = (CLONE_COMPLETE,) = ('2',)
|
||||
CLONE_PAIR_NOT_EXIST = "1073798147"
|
||||
SUPPORT_CLONE_PAIR_VERSION = "V600R003C00"
|
||||
|
@ -64,6 +64,7 @@ class HuaweiConf(object):
|
||||
self._lun_type,
|
||||
self._lun_ready_wait_interval,
|
||||
self._lun_copy_wait_interval,
|
||||
self._lun_copy_speed,
|
||||
self._lun_timeout,
|
||||
self._lun_write_type,
|
||||
self._lun_prefetch,
|
||||
@ -98,7 +99,7 @@ class HuaweiConf(object):
|
||||
need_encode = True
|
||||
|
||||
if need_encode:
|
||||
tree.write(self.conf.cinder_huawei_conf_file, 'UTF-8')
|
||||
tree.write(self.conf.cinder_huawei_conf_file, encoding='UTF-8')
|
||||
|
||||
def _san_address(self, xml_root):
|
||||
text = xml_root.findtext('Storage/RestURL')
|
||||
@ -400,3 +401,19 @@ class HuaweiConf(object):
|
||||
}
|
||||
|
||||
setattr(self.conf, 'replication', config)
|
||||
|
||||
def _lun_copy_speed(self, xml_root):
|
||||
text = xml_root.findtext('LUN/LUNCopySpeed')
|
||||
if text and text.strip() not in constants.LUN_COPY_SPEED_TYPES:
|
||||
msg = (_("Invalid LUNCopySpeed '%(text)s', LUNCopySpeed must "
|
||||
"be between %(low)s and %(high)s.")
|
||||
% {"text": text, "low": constants.LUN_COPY_SPEED_LOW,
|
||||
"high": constants.LUN_COPY_SPEED_HIGHEST})
|
||||
LOG.error(msg)
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
if not text:
|
||||
speed = constants.LUN_COPY_SPEED_MEDIUM
|
||||
else:
|
||||
speed = text.strip()
|
||||
setattr(self.conf, 'lun_copy_speed', int(speed))
|
||||
|
@ -497,3 +497,10 @@ def get_group_type_params(group):
|
||||
opt = get_volume_type_params(volume_type)
|
||||
opts.append(opt)
|
||||
return opts
|
||||
|
||||
|
||||
def is_support_clone_pair(client):
|
||||
array_info = client.get_array_info()
|
||||
version_info = array_info['PRODUCTVERSION']
|
||||
if version_info >= constants.SUPPORT_CLONE_PAIR_VERSION:
|
||||
return True
|
||||
|
@ -48,6 +48,12 @@ class RestClient(object):
|
||||
self.url = None
|
||||
self.device_id = None
|
||||
|
||||
if hasattr(requests, 'packages'):
|
||||
requests.packages.urllib3.disable_warnings(
|
||||
requests.packages.urllib3.exceptions.InsecureRequestWarning)
|
||||
requests.packages.urllib3.disable_warnings(
|
||||
requests.packages.urllib3.exceptions.InsecurePlatformWarning)
|
||||
|
||||
def init_http_head(self):
|
||||
self.url = None
|
||||
self.session = requests.Session()
|
||||
@ -1561,7 +1567,7 @@ class RestClient(object):
|
||||
def extend_lun(self, lun_id, new_volume_size):
|
||||
url = "/lun/expand"
|
||||
data = {"TYPE": 11, "ID": lun_id,
|
||||
"CAPACITY": new_volume_size}
|
||||
"CAPACITY": int(new_volume_size)}
|
||||
result = self.call(url, data, 'PUT')
|
||||
|
||||
msg = _('Extend volume error.')
|
||||
@ -2416,3 +2422,38 @@ class RestClient(object):
|
||||
|
||||
msg = _('Update luns of qos %s error.') % qos_id
|
||||
self._assert_rest_result(result, msg)
|
||||
|
||||
def create_clone_pair(self, source_id, target_id, clone_speed):
|
||||
url = "/clonepair/relation"
|
||||
data = {"copyRate": clone_speed,
|
||||
"sourceID": source_id,
|
||||
"targetID": target_id,
|
||||
"isNeedSynchronize": "0"}
|
||||
result = self.call(url, data, "POST")
|
||||
self._assert_rest_result(result, 'Create ClonePair error, source_id '
|
||||
'is %s.' % source_id)
|
||||
return result['data']['ID']
|
||||
|
||||
def get_clone_pair_info(self, pair_id):
|
||||
url = "/clonepair/%s" % pair_id
|
||||
result = self.call(url, None, "GET")
|
||||
self._assert_rest_result(result, 'Get ClonePair %s error.' % pair_id)
|
||||
return result.get('data', {})
|
||||
|
||||
def sync_clone_pair(self, pair_id):
|
||||
url = "/clonepair/synchronize"
|
||||
data = {"ID": pair_id, "copyAction": 0}
|
||||
result = self.call(url, data, "PUT")
|
||||
self._assert_rest_result(result, 'Sync ClonePair error, pair is %s.'
|
||||
% pair_id)
|
||||
|
||||
def delete_clone_pair(self, pair_id, delete_dst_lun=False):
|
||||
data = {"ID": pair_id,
|
||||
"isDeleteDstLun": delete_dst_lun}
|
||||
url = "/clonepair/%s" % pair_id
|
||||
result = self.call(url, data, "DELETE")
|
||||
if result['error']['code'] == constants.CLONE_PAIR_NOT_EXIST:
|
||||
LOG.warning('ClonePair %s to delete not exist.', pair_id)
|
||||
return
|
||||
self._assert_rest_result(result, 'Delete ClonePair %s error.'
|
||||
% pair_id)
|
||||
|
Loading…
x
Reference in New Issue
Block a user