Huawei: Add manage share snapshot in Huawei driver
Manage share snapshot on the array, before managing the snapshot, make sure that the source share is managed by OpenStack. Implements: blueprint huawei-driver-manage-snapshot Change-Id: I085106dccec5d371771fa112898e980bae182d11
This commit is contained in:
parent
e93004e571
commit
d1303438e9
@ -77,6 +77,10 @@ class HuaweiBase(object):
|
|||||||
def manage_existing(self, share, driver_options):
|
def manage_existing(self, share, driver_options):
|
||||||
"""Manage existing share."""
|
"""Manage existing share."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||||
|
"""Manage existing snapshot."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_network_allocations_number(self):
|
def get_network_allocations_number(self):
|
||||||
"""Get number of network interfaces to be created."""
|
"""Get number of network interfaces to be created."""
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
STATUS_ETH_RUNNING = "10"
|
STATUS_ETH_RUNNING = "10"
|
||||||
STATUS_FS_HEALTH = "1"
|
STATUS_FS_HEALTH = "1"
|
||||||
STATUS_FS_RUNNING = "27"
|
STATUS_FS_RUNNING = "27"
|
||||||
|
STATUS_FSSNAPSHOT_HEALTH = '1'
|
||||||
STATUS_JOIN_DOMAIN = '1'
|
STATUS_JOIN_DOMAIN = '1'
|
||||||
STATUS_EXIT_DOMAIN = '0'
|
STATUS_EXIT_DOMAIN = '0'
|
||||||
STATUS_SERVICE_RUNNING = "2"
|
STATUS_SERVICE_RUNNING = "2"
|
||||||
|
@ -43,7 +43,7 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
"""Huawei Share Driver.
|
"""Huawei Share Driver.
|
||||||
|
|
||||||
Executes commands relating to Shares.
|
Executes commands relating to Shares.
|
||||||
API version history::
|
Driver version history:
|
||||||
|
|
||||||
1.0 - Initial version.
|
1.0 - Initial version.
|
||||||
1.1 - Add shrink share.
|
1.1 - Add shrink share.
|
||||||
@ -56,6 +56,7 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
Add ensure share.
|
Add ensure share.
|
||||||
Add QoS support.
|
Add QoS support.
|
||||||
Add create share from snapshot.
|
Add create share from snapshot.
|
||||||
|
1.3 - Add manage snapshot.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -134,7 +135,8 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
def create_snapshot(self, context, snapshot, share_server=None):
|
def create_snapshot(self, context, snapshot, share_server=None):
|
||||||
"""Create a snapshot."""
|
"""Create a snapshot."""
|
||||||
LOG.debug("Create a snapshot.")
|
LOG.debug("Create a snapshot.")
|
||||||
self.plugin.create_snapshot(snapshot, share_server)
|
snapshot_name = self.plugin.create_snapshot(snapshot, share_server)
|
||||||
|
return {'provider_location': snapshot_name}
|
||||||
|
|
||||||
def delete_snapshot(self, context, snapshot, share_server=None):
|
def delete_snapshot(self, context, snapshot, share_server=None):
|
||||||
"""Delete a snapshot."""
|
"""Delete a snapshot."""
|
||||||
@ -181,6 +183,13 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
driver_options)
|
driver_options)
|
||||||
return {'size': share_size, 'export_locations': location}
|
return {'size': share_size, 'export_locations': location}
|
||||||
|
|
||||||
|
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||||
|
"""Manage existing snapshot."""
|
||||||
|
LOG.debug("Manage existing snapshot to manila.")
|
||||||
|
snapshot_name = self.plugin.manage_existing_snapshot(snapshot,
|
||||||
|
driver_options)
|
||||||
|
return {'provider_location': snapshot_name}
|
||||||
|
|
||||||
def _update_share_stats(self):
|
def _update_share_stats(self):
|
||||||
"""Retrieve status info from share group."""
|
"""Retrieve status info from share group."""
|
||||||
|
|
||||||
@ -188,7 +197,7 @@ class HuaweiNasDriver(driver.ShareDriver):
|
|||||||
data = dict(
|
data = dict(
|
||||||
share_backend_name=backend_name or 'HUAWEI_NAS_Driver',
|
share_backend_name=backend_name or 'HUAWEI_NAS_Driver',
|
||||||
vendor_name='Huawei',
|
vendor_name='Huawei',
|
||||||
driver_version='1.2',
|
driver_version='1.3',
|
||||||
storage_protocol='NFS_CIFS',
|
storage_protocol='NFS_CIFS',
|
||||||
qos=True,
|
qos=True,
|
||||||
total_capacity_gb=0.0,
|
total_capacity_gb=0.0,
|
||||||
|
@ -248,6 +248,7 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
snap_id = self.helper._create_snapshot(sharefsid,
|
snap_id = self.helper._create_snapshot(sharefsid,
|
||||||
snapshot_name)
|
snapshot_name)
|
||||||
LOG.info(_LI('Creating snapshot id %s.'), snap_id)
|
LOG.info(_LI('Creating snapshot id %s.'), snap_id)
|
||||||
|
return snapshot_name.replace("-", "_")
|
||||||
|
|
||||||
def delete_snapshot(self, snapshot, share_server=None):
|
def delete_snapshot(self, snapshot, share_server=None):
|
||||||
"""Delete a snapshot."""
|
"""Delete a snapshot."""
|
||||||
@ -262,7 +263,8 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name)
|
snapshot_id = self.helper._get_snapshot_id(sharefsid, snap_name)
|
||||||
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id)
|
snapshot_info = self.helper._get_snapshot_by_id(snapshot_id)
|
||||||
|
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info)
|
||||||
|
|
||||||
if snapshot_flag:
|
if snapshot_flag:
|
||||||
self.helper._delete_snapshot(snapshot_id)
|
self.helper._delete_snapshot(snapshot_id)
|
||||||
@ -358,7 +360,8 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
name=snapshot['share_name'])
|
name=snapshot['share_name'])
|
||||||
|
|
||||||
snapshot_id = self.helper._get_snapshot_id(share_fs_id, snapshot['id'])
|
snapshot_id = self.helper._get_snapshot_id(share_fs_id, snapshot['id'])
|
||||||
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_id)
|
snapshot_info = self.helper._get_snapshot_by_id(snapshot_id)
|
||||||
|
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info)
|
||||||
if not snapshot_flag:
|
if not snapshot_flag:
|
||||||
err_msg = (_("Cannot find snapshot %s on array.")
|
err_msg = (_("Cannot find snapshot %s on array.")
|
||||||
% snapshot['snapshot_id'])
|
% snapshot['snapshot_id'])
|
||||||
@ -873,6 +876,51 @@ class V3StorageConnection(driver.HuaweiBase):
|
|||||||
location = self._get_location_path(share_name, share_proto)
|
location = self._get_location_path(share_name, share_proto)
|
||||||
return (share_size, [location])
|
return (share_size, [location])
|
||||||
|
|
||||||
|
def _check_snapshot_valid_for_manage(self, snapshot_info):
|
||||||
|
snapshot_name = snapshot_info['data']['NAME']
|
||||||
|
|
||||||
|
# Check whether the snapshot is normal.
|
||||||
|
if (snapshot_info['data']['HEALTHSTATUS']
|
||||||
|
!= constants.STATUS_FSSNAPSHOT_HEALTH):
|
||||||
|
msg = (_("Can't import snapshot %(snapshot)s to Manila. "
|
||||||
|
"Snapshot status is not normal, snapshot status: "
|
||||||
|
"%(status)s.")
|
||||||
|
% {'snapshot': snapshot_name,
|
||||||
|
'status': snapshot_info['data']['HEALTHSTATUS']})
|
||||||
|
raise exception.ManageInvalidShareSnapshot(
|
||||||
|
reason=msg)
|
||||||
|
|
||||||
|
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||||
|
"""Manage existing snapshot."""
|
||||||
|
|
||||||
|
share_proto = snapshot['share']['share_proto']
|
||||||
|
share_url_type = self.helper._get_share_url_type(share_proto)
|
||||||
|
share_storage = self.helper._get_share_by_name(snapshot['share_name'],
|
||||||
|
share_url_type)
|
||||||
|
if not share_storage:
|
||||||
|
err_msg = (_("Failed to import snapshot %(snapshot)s to Manila. "
|
||||||
|
"Snapshot source share %(share)s doesn't exist "
|
||||||
|
"on array.")
|
||||||
|
% {'snapshot': snapshot['provider_location'],
|
||||||
|
'share': snapshot['share_name']})
|
||||||
|
raise exception.InvalidShare(reason=err_msg)
|
||||||
|
sharefsid = share_storage['FSID']
|
||||||
|
|
||||||
|
provider_location = snapshot.get('provider_location')
|
||||||
|
snapshot_id = sharefsid + "@" + provider_location
|
||||||
|
snapshot_info = self.helper._get_snapshot_by_id(snapshot_id)
|
||||||
|
snapshot_flag = self.helper._check_snapshot_id_exist(snapshot_info)
|
||||||
|
if not snapshot_flag:
|
||||||
|
err_msg = (_("Cannot find snapshot %s on array.")
|
||||||
|
% snapshot['provider_location'])
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=err_msg)
|
||||||
|
else:
|
||||||
|
self._check_snapshot_valid_for_manage(snapshot_info)
|
||||||
|
snapshot_name = ("share_snapshot_"
|
||||||
|
+ snapshot['id'].replace("-", "_"))
|
||||||
|
self.helper._rename_share_snapshot(snapshot_id, snapshot_name)
|
||||||
|
return snapshot_name
|
||||||
|
|
||||||
def check_retype_change_opts(self, opts, poolinfo, fs):
|
def check_retype_change_opts(self, opts, poolinfo, fs):
|
||||||
change_opts = {
|
change_opts = {
|
||||||
"partitionid": None,
|
"partitionid": None,
|
||||||
|
@ -588,23 +588,25 @@ class RestHelper(object):
|
|||||||
|
|
||||||
return share_client_type
|
return share_client_type
|
||||||
|
|
||||||
def _check_snapshot_id_exist(self, snap_id):
|
def _check_snapshot_id_exist(self, snapshot_info):
|
||||||
"""Check the snapshot id exists."""
|
"""Check the snapshot id exists."""
|
||||||
url_subfix = "/FSSNAPSHOT/" + snap_id
|
|
||||||
|
|
||||||
url = url_subfix
|
if snapshot_info['error']['code'] == constants.MSG_SNAPSHOT_NOT_FOUND:
|
||||||
result = self.call(url, None, "GET")
|
|
||||||
|
|
||||||
if result['error']['code'] == constants.MSG_SNAPSHOT_NOT_FOUND:
|
|
||||||
return False
|
return False
|
||||||
elif result['error']['code'] == 0:
|
elif snapshot_info['error']['code'] == 0:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
err_str = "Check the snapshot id exists error!"
|
err_str = "Check the snapshot id exists error!"
|
||||||
err_msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
|
err_msg = (_('%(err)s\nresult: %(res)s.') % {'err': err_str,
|
||||||
'res': result})
|
'res': snapshot_info})
|
||||||
LOG.error(err_msg)
|
raise exception.InvalidShareSnapshot(reason=err_msg)
|
||||||
raise exception.InvalidShare(reason=err_msg)
|
|
||||||
|
def _get_snapshot_by_id(self, snap_id):
|
||||||
|
"""Get snapshot by id"""
|
||||||
|
url = "/FSSNAPSHOT/" + snap_id
|
||||||
|
|
||||||
|
result = self.call(url, None, "GET")
|
||||||
|
return result
|
||||||
|
|
||||||
def _delete_snapshot(self, snap_id):
|
def _delete_snapshot(self, snap_id):
|
||||||
"""Deletes snapshot."""
|
"""Deletes snapshot."""
|
||||||
@ -851,6 +853,14 @@ class RestHelper(object):
|
|||||||
self._assert_rest_result(result,
|
self._assert_rest_result(result,
|
||||||
_('Remove filesystem from partition error.'))
|
_('Remove filesystem from partition error.'))
|
||||||
|
|
||||||
|
def _rename_share_snapshot(self, snapshot_id, new_name):
|
||||||
|
url = "/FSSNAPSHOT/" + snapshot_id
|
||||||
|
data = jsonutils.dumps({"NAME": new_name})
|
||||||
|
result = self.call(url, data, "PUT")
|
||||||
|
msg = _('Rename share snapshot on array error.')
|
||||||
|
self._assert_rest_result(result, msg)
|
||||||
|
self._assert_data_in_result(result, msg)
|
||||||
|
|
||||||
def _get_cache_id_by_name(self, name):
|
def _get_cache_id_by_name(self, name):
|
||||||
url = "/SMARTCACHEPARTITION"
|
url = "/SMARTCACHEPARTITION"
|
||||||
result = self.call(url, None, "GET")
|
result = self.call(url, None, "GET")
|
||||||
|
@ -450,11 +450,21 @@ class FakeHuaweiNasHelper(helper.RestHelper):
|
|||||||
|
|
||||||
if url == "/FSSNAPSHOT/4@share_snapshot_fake_snapshot_uuid":
|
if url == "/FSSNAPSHOT/4@share_snapshot_fake_snapshot_uuid":
|
||||||
if self.snapshot_flag:
|
if self.snapshot_flag:
|
||||||
data = """{"error":{"code":0},"data":{"ID":"3"}}"""
|
data = """{"error":{"code":0},
|
||||||
|
"data":{"ID":"4@share_snapshot_fake_snapshot_uuid"}}"""
|
||||||
else:
|
else:
|
||||||
data = '{"error":{"code":1073754118}}'
|
data = '{"error":{"code":1073754118}}'
|
||||||
self.delete_flag = True
|
self.delete_flag = True
|
||||||
|
|
||||||
|
if url == "/FSSNAPSHOT/4@fake_storage_snapshot_name":
|
||||||
|
if self.snapshot_flag:
|
||||||
|
data = """{"error":{"code":0},
|
||||||
|
"data":{"ID":"4@share_snapshot_fake_snapshot_uuid",
|
||||||
|
"NAME":"share_snapshot_fake_snapshot_uuid",
|
||||||
|
"HEALTHSTATUS":"1"}}"""
|
||||||
|
else:
|
||||||
|
data = '{"error":{"code":1073754118}}'
|
||||||
|
|
||||||
if url == "/FSSNAPSHOT/3":
|
if url == "/FSSNAPSHOT/3":
|
||||||
data = """{"error":{"code":0}}"""
|
data = """{"error":{"code":0}}"""
|
||||||
self.delete_flag = True
|
self.delete_flag = True
|
||||||
@ -943,6 +953,40 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.storage_nfs_snapshot = {
|
||||||
|
'id': 'fake_snapshot_uuid',
|
||||||
|
'snapshot_id': 'fake_snapshot_uuid',
|
||||||
|
'display_name': 'snapshot',
|
||||||
|
'name': 'fake_snapshot_name',
|
||||||
|
'provider_location': 'fake_storage_snapshot_name',
|
||||||
|
'size': 1,
|
||||||
|
'share_name': 'share_fake_uuid',
|
||||||
|
'share_id': 'fake_uuid',
|
||||||
|
'share': {
|
||||||
|
'share_name': 'share_fake_uuid',
|
||||||
|
'share_id': 'fake_uuid',
|
||||||
|
'share_size': 1,
|
||||||
|
'share_proto': 'NFS',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.storage_cifs_snapshot = {
|
||||||
|
'id': 'fake_snapshot_uuid',
|
||||||
|
'snapshot_id': 'fake_snapshot_uuid',
|
||||||
|
'display_name': 'snapshot',
|
||||||
|
'name': 'fake_snapshot_name',
|
||||||
|
'provider_location': 'fake_storage_snapshot_name',
|
||||||
|
'size': 1,
|
||||||
|
'share_name': 'share_fake_uuid',
|
||||||
|
'share_id': 'fake_uuid',
|
||||||
|
'share': {
|
||||||
|
'share_name': 'share_fake_uuid',
|
||||||
|
'share_id': 'fake_uuid',
|
||||||
|
'share_size': 1,
|
||||||
|
'share_proto': 'CIFS',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
self.security_service = {
|
self.security_service = {
|
||||||
'id': 'fake_id',
|
'id': 'fake_id',
|
||||||
'domain': 'FAKE',
|
'domain': 'FAKE',
|
||||||
@ -1762,12 +1806,14 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
self.assertTrue(self.driver.plugin.helper.delete_flag)
|
||||||
|
|
||||||
def test_check_snapshot_id_exist_fail(self):
|
def test_check_snapshot_id_exist_fail(self):
|
||||||
snapshot_id = "4"
|
snapshot_id = "4@share_snapshot_not_exist"
|
||||||
self.driver.plugin.helper.login()
|
self.driver.plugin.helper.login()
|
||||||
self.driver.plugin.helper.test_normal = False
|
self.driver.plugin.helper.test_normal = False
|
||||||
self.assertRaises(exception.InvalidShare,
|
snapshot_info = self.driver.plugin.helper._get_snapshot_by_id(
|
||||||
|
snapshot_id)
|
||||||
|
self.assertRaises(exception.InvalidShareSnapshot,
|
||||||
self.driver.plugin.helper._check_snapshot_id_exist,
|
self.driver.plugin.helper._check_snapshot_id_exist,
|
||||||
snapshot_id)
|
snapshot_info)
|
||||||
|
|
||||||
def test_delete_share_nfs_fail_not_exist(self):
|
def test_delete_share_nfs_fail_not_exist(self):
|
||||||
self.driver.plugin.helper.login()
|
self.driver.plugin.helper.login()
|
||||||
@ -2135,7 +2181,7 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
expected["share_backend_name"] = "fake_share_backend_name"
|
expected["share_backend_name"] = "fake_share_backend_name"
|
||||||
expected["driver_handles_share_servers"] = False
|
expected["driver_handles_share_servers"] = False
|
||||||
expected["vendor_name"] = 'Huawei'
|
expected["vendor_name"] = 'Huawei'
|
||||||
expected["driver_version"] = '1.2'
|
expected["driver_version"] = '1.3'
|
||||||
expected["storage_protocol"] = 'NFS_CIFS'
|
expected["storage_protocol"] = 'NFS_CIFS'
|
||||||
expected['reserved_percentage'] = 0
|
expected['reserved_percentage'] = 0
|
||||||
expected['total_capacity_gb'] = 0.0
|
expected['total_capacity_gb'] = 0.0
|
||||||
@ -2776,6 +2822,56 @@ class HuaweiShareDriverTestCase(test.TestCase):
|
|||||||
self.share_nfs,
|
self.share_nfs,
|
||||||
self.driver_options)
|
self.driver_options)
|
||||||
|
|
||||||
|
@ddt.data({"share_proto": "NFS",
|
||||||
|
"provider_location": "share_snapshot_fake_snapshot_uuid"},
|
||||||
|
{"share_proto": "CIFS",
|
||||||
|
"provider_location": "share_snapshot_fake_snapshot_uuid"})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_manage_existing_snapshot_success(self, share_proto,
|
||||||
|
provider_location):
|
||||||
|
if share_proto == "NFS":
|
||||||
|
snapshot = self.storage_nfs_snapshot
|
||||||
|
elif share_proto == "CIFS":
|
||||||
|
snapshot = self.storage_cifs_snapshot
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
|
snapshot_info = self.driver.manage_existing_snapshot(
|
||||||
|
snapshot, self.driver_options)
|
||||||
|
self.assertEqual(provider_location, snapshot_info['provider_location'])
|
||||||
|
|
||||||
|
def test_manage_existing_snapshot_share_not_exist(self):
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
|
self.mock_object(self.driver.plugin.helper,
|
||||||
|
'_get_share_by_name',
|
||||||
|
mock.Mock(return_value={}))
|
||||||
|
self.assertRaises(exception.InvalidShare,
|
||||||
|
self.driver.manage_existing_snapshot,
|
||||||
|
self.storage_nfs_snapshot,
|
||||||
|
self.driver_options)
|
||||||
|
|
||||||
|
def test_manage_existing_snapshot_sharesnapshot_not_exist(self):
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
|
self.mock_object(self.driver.plugin.helper,
|
||||||
|
'_check_snapshot_id_exist',
|
||||||
|
mock.Mock(return_value={}))
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self.driver.manage_existing_snapshot,
|
||||||
|
self.storage_nfs_snapshot,
|
||||||
|
self.driver_options)
|
||||||
|
|
||||||
|
def test_manage_existing_snapshot_sharesnapshot_not_normal(self):
|
||||||
|
snapshot_info = {"error": {"code": 0},
|
||||||
|
"data": {"ID": "4@share_snapshot_fake_snapshot_uuid",
|
||||||
|
"NAME": "share_snapshot_fake_snapshot_uuid",
|
||||||
|
"HEALTHSTATUS": "2"}}
|
||||||
|
self.driver.plugin.helper.login()
|
||||||
|
self.mock_object(self.driver.plugin.helper,
|
||||||
|
'_get_snapshot_by_id',
|
||||||
|
mock.Mock(return_value=snapshot_info))
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self.driver.manage_existing_snapshot,
|
||||||
|
self.storage_nfs_snapshot,
|
||||||
|
self.driver_options)
|
||||||
|
|
||||||
def test_get_pool_success(self):
|
def test_get_pool_success(self):
|
||||||
self.driver.plugin.helper.login()
|
self.driver.plugin.helper.login()
|
||||||
pool_name = self.driver.get_pool(self.share_nfs_host_not_exist)
|
pool_name = self.driver.get_pool(self.share_nfs_host_not_exist)
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Manage share snapshot on array in huawei driver.
|
Loading…
x
Reference in New Issue
Block a user