Merge "Add support for manage/unmanage snapshots in HNAS driver"
This commit is contained in:
commit
e8af5394b8
@ -53,7 +53,7 @@ Mapping of share drivers and share features support
|
|||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||||
| HDFS | K | \- | M | \- | K | K | \- |
|
| HDFS | K | \- | M | \- | K | K | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||||
| Hitachi HNAS | L | L | L | M | L | L | \- |
|
| Hitachi HNAS | L | L | L | M | L | L | O |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||||
| Hitachi HSP | N | N | N | N | \- | \- | \- |
|
| Hitachi HSP | N | N | N | N | \- | \- | \- |
|
||||||
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
+----------------------------------------+-----------------------+-----------------------+--------------+--------------+------------------------+----------------------------+--------------------------+
|
||||||
|
@ -358,14 +358,18 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
"""
|
"""
|
||||||
hnas_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
hnas_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
||||||
|
|
||||||
LOG.debug("The snapshot of share %(ss_sid)s will be created with "
|
LOG.debug("The snapshot of share %(snap_share_id)s will be created "
|
||||||
"id %(ss_id)s.", {'ss_sid': snapshot['share_id'],
|
"with id %(snap_id)s.",
|
||||||
'ss_id': snapshot['id']})
|
{'snap_share_id': snapshot['share_id'],
|
||||||
|
'snap_id': snapshot['id']})
|
||||||
|
|
||||||
self._create_snapshot(hnas_share_id, snapshot)
|
self._create_snapshot(hnas_share_id, snapshot)
|
||||||
LOG.info(_LI("Snapshot %(id)s successfully created."),
|
LOG.info(_LI("Snapshot %(id)s successfully created."),
|
||||||
{'id': snapshot['id']})
|
{'id': snapshot['id']})
|
||||||
|
|
||||||
|
return {'provider_location': os.path.join('/snapshots', hnas_share_id,
|
||||||
|
snapshot['id'])}
|
||||||
|
|
||||||
def delete_snapshot(self, context, snapshot, share_server=None):
|
def delete_snapshot(self, context, snapshot, share_server=None):
|
||||||
"""Deletes snapshot.
|
"""Deletes snapshot.
|
||||||
|
|
||||||
@ -375,12 +379,15 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
Not used by this driver.
|
Not used by this driver.
|
||||||
"""
|
"""
|
||||||
hnas_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
hnas_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
||||||
|
hnas_snapshot_id = self._get_hnas_snapshot_id(snapshot)
|
||||||
|
|
||||||
LOG.debug("The snapshot %(ss_sid)s will be deleted. The related "
|
LOG.debug("The snapshot %(snap_id)s will be deleted. The related "
|
||||||
"share ID is %(ss_id)s.",
|
"share ID is %(snap_share_id)s.",
|
||||||
{'ss_sid': snapshot['share_id'], 'ss_id': snapshot['id']})
|
{'snap_id': snapshot['id'],
|
||||||
|
'snap_share_id': snapshot['share_id']})
|
||||||
|
|
||||||
|
self._delete_snapshot(hnas_share_id, hnas_snapshot_id)
|
||||||
|
|
||||||
self._delete_snapshot(hnas_share_id, snapshot['id'])
|
|
||||||
LOG.info(_LI("Snapshot %(id)s successfully deleted."),
|
LOG.info(_LI("Snapshot %(id)s successfully deleted."),
|
||||||
{'id': snapshot['id']})
|
{'id': snapshot['id']})
|
||||||
|
|
||||||
@ -432,9 +439,10 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
{'ss_id': snapshot['id']})
|
{'ss_id': snapshot['id']})
|
||||||
|
|
||||||
hnas_src_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
hnas_src_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
||||||
|
hnas_src_snap_id = self._get_hnas_snapshot_id(snapshot)
|
||||||
|
|
||||||
export_list = self._create_share_from_snapshot(
|
export_list = self._create_share_from_snapshot(
|
||||||
share, hnas_src_share_id, snapshot)
|
share, hnas_src_share_id, hnas_src_snap_id)
|
||||||
|
|
||||||
LOG.debug("Share %(share)s created successfully on path(s): "
|
LOG.debug("Share %(share)s created successfully on path(s): "
|
||||||
"%(paths)s.",
|
"%(paths)s.",
|
||||||
@ -707,6 +715,18 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
|
|
||||||
return hnas_id
|
return hnas_id
|
||||||
|
|
||||||
|
def _get_hnas_snapshot_id(self, snapshot):
|
||||||
|
hnas_snapshot_id = snapshot['id']
|
||||||
|
|
||||||
|
if snapshot['provider_location']:
|
||||||
|
LOG.debug("Snapshot %(snap_id)s with provider_location: "
|
||||||
|
"%(p_loc)s.",
|
||||||
|
{'snap_id': hnas_snapshot_id,
|
||||||
|
'p_loc': snapshot['provider_location']})
|
||||||
|
hnas_snapshot_id = snapshot['provider_location'].split('/')[-1]
|
||||||
|
|
||||||
|
return hnas_snapshot_id
|
||||||
|
|
||||||
def _create_share(self, share_id, share_size, share_proto):
|
def _create_share(self, share_id, share_size, share_proto):
|
||||||
"""Creates share.
|
"""Creates share.
|
||||||
|
|
||||||
@ -914,7 +934,8 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
path = os.path.join('/snapshots', hnas_share_id)
|
path = os.path.join('/snapshots', hnas_share_id)
|
||||||
self.hnas.delete_directory(path)
|
self.hnas.delete_directory(path)
|
||||||
|
|
||||||
def _create_share_from_snapshot(self, share, src_hnas_share_id, snapshot):
|
def _create_share_from_snapshot(self, share, src_hnas_share_id,
|
||||||
|
hnas_snapshot_id):
|
||||||
"""Creates a new share from snapshot.
|
"""Creates a new share from snapshot.
|
||||||
|
|
||||||
It copies everything from snapshot directory to a new vvol,
|
It copies everything from snapshot directory to a new vvol,
|
||||||
@ -922,14 +943,14 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
:param share: a dict from new share.
|
:param share: a dict from new share.
|
||||||
:param src_hnas_share_id: HNAS ID of share from which snapshot was
|
:param src_hnas_share_id: HNAS ID of share from which snapshot was
|
||||||
taken.
|
taken.
|
||||||
:param snapshot: a dict from snapshot that will be copied to
|
:param hnas_snapshot_id: HNAS ID from snapshot that will be copied to
|
||||||
new share.
|
new share.
|
||||||
:returns: Returns a list of dicts containing the new share's export
|
:returns: Returns a list of dicts containing the new share's export
|
||||||
locations.
|
locations.
|
||||||
"""
|
"""
|
||||||
dest_path = os.path.join('/shares', share['id'])
|
dest_path = os.path.join('/shares', share['id'])
|
||||||
src_path = os.path.join('/snapshots', src_hnas_share_id,
|
src_path = os.path.join('/snapshots', src_hnas_share_id,
|
||||||
snapshot['id'])
|
hnas_snapshot_id)
|
||||||
|
|
||||||
# Before copying everything to new vvol, we need to create it,
|
# Before copying everything to new vvol, we need to create it,
|
||||||
# because we only can transform an empty directory into a vvol.
|
# because we only can transform an empty directory into a vvol.
|
||||||
@ -959,7 +980,7 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
msg = _LE('Failed to create share %(share_id)s from snapshot '
|
msg = _LE('Failed to create share %(share_id)s from snapshot '
|
||||||
'%(snap)s.')
|
'%(snap)s.')
|
||||||
LOG.exception(msg, {'share_id': share['id'],
|
LOG.exception(msg, {'share_id': share['id'],
|
||||||
'snap': snapshot['id']})
|
'snap': hnas_snapshot_id})
|
||||||
self.hnas.vvol_delete(share['id'])
|
self.hnas.vvol_delete(share['id'])
|
||||||
|
|
||||||
return self._get_export_locations(
|
return self._get_export_locations(
|
||||||
@ -992,3 +1013,78 @@ class HitachiHNASDriver(driver.ShareDriver):
|
|||||||
else:
|
else:
|
||||||
export = r'\\%s\%s' % (ip, hnas_share_id)
|
export = r'\\%s\%s' % (ip, hnas_share_id)
|
||||||
return export
|
return export
|
||||||
|
|
||||||
|
def manage_existing_snapshot(self, snapshot, driver_options):
|
||||||
|
"""Manages a snapshot that exists only in HNAS.
|
||||||
|
|
||||||
|
The snapshot to be managed should be in the path
|
||||||
|
/snapshots/SHARE_ID/SNAPSHOT_ID. Also, the size of snapshot should be
|
||||||
|
provided as --driver_options size=<size>.
|
||||||
|
:param snapshot: snapshot that will be managed.
|
||||||
|
:param driver_options: expects only one key 'size'. It must be
|
||||||
|
provided in order to manage a snapshot.
|
||||||
|
|
||||||
|
:returns: Returns a dict with size of snapshot managed
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
snapshot_size = int(driver_options.get("size", 0))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
msg = _("The size in driver options to manage snapshot "
|
||||||
|
"%(snap_id)s should be an integer, in format "
|
||||||
|
"driver-options size=<SIZE>. Value passed: "
|
||||||
|
"%(size)s.") % {'snap_id': snapshot['id'],
|
||||||
|
'size': driver_options.get("size")}
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=msg)
|
||||||
|
|
||||||
|
if snapshot_size == 0:
|
||||||
|
msg = _("Snapshot %(snap_id)s has no size specified for manage. "
|
||||||
|
"Please, provide the size with parameter driver-options "
|
||||||
|
"size=<SIZE>.") % {'snap_id': snapshot['id']}
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=msg)
|
||||||
|
|
||||||
|
hnas_share_id = self._get_hnas_share_id(snapshot['share_id'])
|
||||||
|
|
||||||
|
LOG.debug("Path provided to manage snapshot: %(path)s.",
|
||||||
|
{'path': snapshot['provider_location']})
|
||||||
|
|
||||||
|
path_info = snapshot['provider_location'].split('/')
|
||||||
|
|
||||||
|
if len(path_info) == 4 and path_info[1] == 'snapshots':
|
||||||
|
path_share_id = path_info[2]
|
||||||
|
hnas_snapshot_id = path_info[3]
|
||||||
|
else:
|
||||||
|
msg = (_("Incorrect path %(path)s for manage snapshot "
|
||||||
|
"%(snap_id)s. It should have the following format: "
|
||||||
|
"/snapshots/SHARE_ID/SNAPSHOT_ID.") %
|
||||||
|
{'path': snapshot['provider_location'],
|
||||||
|
'snap_id': snapshot['id']})
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=msg)
|
||||||
|
|
||||||
|
if hnas_share_id != path_share_id:
|
||||||
|
msg = _("The snapshot %(snap_id)s does not belong to share "
|
||||||
|
"%(share_id)s.") % {'snap_id': snapshot['id'],
|
||||||
|
'share_id': snapshot['share_id']}
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=msg)
|
||||||
|
|
||||||
|
if not self.hnas.check_snapshot(snapshot['provider_location']):
|
||||||
|
msg = _("Snapshot %(snap_id)s does not exist in "
|
||||||
|
"HNAS.") % {'snap_id': hnas_snapshot_id}
|
||||||
|
raise exception.ManageInvalidShareSnapshot(reason=msg)
|
||||||
|
|
||||||
|
LOG.info(_LI("Snapshot %(snap_path)s for share %(shr_id)s was "
|
||||||
|
"successfully managed with ID %(snap_id)s."),
|
||||||
|
{'snap_path': snapshot['provider_location'],
|
||||||
|
'shr_id': snapshot['share_id'], 'snap_id': snapshot['id']})
|
||||||
|
|
||||||
|
return {'size': snapshot_size}
|
||||||
|
|
||||||
|
def unmanage_snapshot(self, snapshot):
|
||||||
|
"""Unmanage a share snapshot
|
||||||
|
|
||||||
|
:param snapshot: Snapshot that will be unmanaged.
|
||||||
|
"""
|
||||||
|
LOG.info(_LI("The snapshot with ID %(snap_id)s from share "
|
||||||
|
"%(share_id)s is no longer being managed by Manila. "
|
||||||
|
"However, it is not deleted and can be found in HNAS."),
|
||||||
|
{'snap_id': snapshot['id'],
|
||||||
|
'share_id': snapshot['share_id']})
|
||||||
|
@ -239,7 +239,7 @@ class HNASSSHBackend(object):
|
|||||||
job_status.files_missing) == ("Job was completed",
|
job_status.files_missing) == ("Job was completed",
|
||||||
"Success", '0', '0'):
|
"Success", '0', '0'):
|
||||||
|
|
||||||
LOG.debug("Snapshot of source path %(src)s to destination"
|
LOG.debug("Snapshot of source path %(src)s to destination "
|
||||||
"path %(dest)s created successfully.",
|
"path %(dest)s created successfully.",
|
||||||
{'src': src_path,
|
{'src': src_path,
|
||||||
'dest': dest_path})
|
'dest': dest_path})
|
||||||
@ -270,6 +270,20 @@ class HNASSSHBackend(object):
|
|||||||
def delete_directory(self, path):
|
def delete_directory(self, path):
|
||||||
self._locked_selectfs('delete', path)
|
self._locked_selectfs('delete', path)
|
||||||
|
|
||||||
|
def check_snapshot(self, path):
|
||||||
|
command = ['path-to-object-number', '-f', self.fs_name, path]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._execute(command)
|
||||||
|
except processutils.ProcessExecutionError as e:
|
||||||
|
if 'Unable to locate component:' in e.stdout:
|
||||||
|
LOG.debug("Cannot find %(path)s: %(out)s",
|
||||||
|
{'path': path, 'out': e.stdout})
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
return True
|
||||||
|
|
||||||
def check_fs_mounted(self):
|
def check_fs_mounted(self):
|
||||||
command = ['df', '-a', '-f', self.fs_name]
|
command = ['df', '-a', '-f', self.fs_name]
|
||||||
output, err = self._execute(command)
|
output, err = self._execute(command)
|
||||||
|
@ -93,12 +93,24 @@ snapshot_nfs = {
|
|||||||
'id': 'abba6d9b-f29c-4bf7-aac1-618cda7aaf0f',
|
'id': 'abba6d9b-f29c-4bf7-aac1-618cda7aaf0f',
|
||||||
'share_id': 'aa4a7710-f326-41fb-ad18-b4ad587fc87a',
|
'share_id': 'aa4a7710-f326-41fb-ad18-b4ad587fc87a',
|
||||||
'share': share_nfs,
|
'share': share_nfs,
|
||||||
|
'provider_location': '/snapshots/aa4a7710-f326-41fb-ad18-b4ad587fc87a/'
|
||||||
|
'abba6d9b-f29c-4bf7-aac1-618cda7aaf0f',
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot_cifs = {
|
snapshot_cifs = {
|
||||||
'id': '91bc6e1b-1ba5-f29c-abc1-da7618cabf0a',
|
'id': '91bc6e1b-1ba5-f29c-abc1-da7618cabf0a',
|
||||||
'share_id': 'f5cadaf2-afbe-4cc4-9021-85491b6b76f7',
|
'share_id': 'f5cadaf2-afbe-4cc4-9021-85491b6b76f7',
|
||||||
'share': share_cifs,
|
'share': share_cifs,
|
||||||
|
'provider_location': '/snapshots/f5cadaf2-afbe-4cc4-9021-85491b6b76f7/'
|
||||||
|
'91bc6e1b-1ba5-f29c-abc1-da7618cabf0a',
|
||||||
|
}
|
||||||
|
|
||||||
|
manage_snapshot = {
|
||||||
|
'id': 'bc168eb-fa71-beef-153a-3d451aa1351f',
|
||||||
|
'share_id': 'aa4a7710-f326-41fb-ad18-b4ad587fc87a',
|
||||||
|
'share': share_nfs,
|
||||||
|
'provider_location': '/snapshots/aa4a7710-f326-41fb-ad18-b4ad587fc87a'
|
||||||
|
'/snapshot18-05-2106',
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid_share = {
|
invalid_share = {
|
||||||
@ -433,6 +445,9 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
@ddt.data(snapshot_nfs, snapshot_cifs)
|
@ddt.data(snapshot_nfs, snapshot_cifs)
|
||||||
def test_create_snapshot(self, snapshot):
|
def test_create_snapshot(self, snapshot):
|
||||||
hnas_id = snapshot['share_id']
|
hnas_id = snapshot['share_id']
|
||||||
|
p_location = {'provider_location': '/snapshots/' + hnas_id + '/' +
|
||||||
|
snapshot['id']}
|
||||||
|
|
||||||
self.mock_object(ssh.HNASSSHBackend, "get_nfs_host_list", mock.Mock(
|
self.mock_object(ssh.HNASSSHBackend, "get_nfs_host_list", mock.Mock(
|
||||||
return_value=['172.24.44.200(rw)']))
|
return_value=['172.24.44.200(rw)']))
|
||||||
self.mock_object(ssh.HNASSSHBackend, "update_nfs_access_rule",
|
self.mock_object(ssh.HNASSSHBackend, "update_nfs_access_rule",
|
||||||
@ -441,11 +456,12 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
return_value=False))
|
return_value=False))
|
||||||
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock())
|
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock())
|
||||||
|
|
||||||
self._driver.create_snapshot('context', snapshot)
|
out = self._driver.create_snapshot('context', snapshot)
|
||||||
|
|
||||||
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
|
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
|
||||||
'/shares/' + hnas_id, '/snapshots/' + hnas_id + '/' +
|
'/shares/' + hnas_id, '/snapshots/' + hnas_id + '/' +
|
||||||
snapshot['id'])
|
snapshot['id'])
|
||||||
|
self.assertEqual(p_location, out)
|
||||||
|
|
||||||
if snapshot['share']['share_proto'].lower() == 'nfs':
|
if snapshot['share']['share_proto'].lower() == 'nfs':
|
||||||
ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with(
|
ssh.HNASSSHBackend.get_nfs_host_list.assert_called_once_with(
|
||||||
@ -516,6 +532,22 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
ssh.HNASSSHBackend.delete_directory.assert_called_once_with(
|
ssh.HNASSSHBackend.delete_directory.assert_called_once_with(
|
||||||
'/snapshots/' + hnas_id)
|
'/snapshots/' + hnas_id)
|
||||||
|
|
||||||
|
def test_delete_managed_snapshot(self):
|
||||||
|
hnas_id = manage_snapshot['share_id']
|
||||||
|
self.mock_object(driver.HitachiHNASDriver, "_check_fs_mounted")
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, "tree_delete")
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, "delete_directory")
|
||||||
|
|
||||||
|
self._driver.delete_snapshot('context', manage_snapshot)
|
||||||
|
|
||||||
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
self.assertTrue(self.mock_log.info.called)
|
||||||
|
driver.HitachiHNASDriver._check_fs_mounted.assert_called_once_with()
|
||||||
|
ssh.HNASSSHBackend.tree_delete.assert_called_once_with(
|
||||||
|
manage_snapshot['provider_location'])
|
||||||
|
ssh.HNASSSHBackend.delete_directory.assert_called_once_with(
|
||||||
|
'/snapshots/' + hnas_id)
|
||||||
|
|
||||||
@ddt.data(share_nfs, share_cifs)
|
@ddt.data(share_nfs, share_cifs)
|
||||||
def test_ensure_share(self, share):
|
def test_ensure_share(self, share):
|
||||||
result = self._driver.ensure_share('context', share)
|
result = self._driver.ensure_share('context', share)
|
||||||
@ -817,3 +849,52 @@ class HitachiHNASTestCase(test.TestCase):
|
|||||||
(manila.share.driver.ShareDriver._update_share_stats.
|
(manila.share.driver.ShareDriver._update_share_stats.
|
||||||
assert_called_once_with(fake_data))
|
assert_called_once_with(fake_data))
|
||||||
self.assertTrue(self.mock_log.info.called)
|
self.assertTrue(self.mock_log.info.called)
|
||||||
|
|
||||||
|
def test_manage_existing_snapshot(self):
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, 'check_snapshot',
|
||||||
|
mock.Mock(return_value=True))
|
||||||
|
|
||||||
|
out = self._driver.manage_existing_snapshot(manage_snapshot,
|
||||||
|
{'size': 20})
|
||||||
|
|
||||||
|
ssh.HNASSSHBackend.check_snapshot.assert_called_with(
|
||||||
|
'/snapshots/aa4a7710-f326-41fb-ad18-b4ad587fc87a'
|
||||||
|
'/snapshot18-05-2106')
|
||||||
|
self.assertEqual(20, out['size'])
|
||||||
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
self.assertTrue(self.mock_log.info.called)
|
||||||
|
|
||||||
|
@ddt.data('fake_size', '128GB', '512 GB', {'size': 128})
|
||||||
|
def test_manage_snapshot_invalid_size_exception(self, size):
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self._driver.manage_existing_snapshot,
|
||||||
|
manage_snapshot, {'size': size})
|
||||||
|
|
||||||
|
def test_manage_snapshot_size_not_provided_exception(self):
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self._driver.manage_existing_snapshot,
|
||||||
|
manage_snapshot, {})
|
||||||
|
|
||||||
|
@ddt.data('/root/snapshot_id', '/snapshots/share1/snapshot_id',
|
||||||
|
'/directory1', 'snapshots/share1/snapshot_id')
|
||||||
|
def test_manage_snapshot_invalid_path_exception(self, path):
|
||||||
|
snap_copy = manage_snapshot.copy()
|
||||||
|
snap_copy['provider_location'] = path
|
||||||
|
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self._driver.manage_existing_snapshot,
|
||||||
|
snap_copy, {'size': 20})
|
||||||
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
|
def test_manage_inexistent_snapshot_exception(self):
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, 'check_snapshot',
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
|
self.assertRaises(exception.ManageInvalidShareSnapshot,
|
||||||
|
self._driver.manage_existing_snapshot,
|
||||||
|
manage_snapshot, {'size': 20})
|
||||||
|
self.assertTrue(self.mock_log.debug.called)
|
||||||
|
|
||||||
|
def test_unmanage_snapshot(self):
|
||||||
|
self._driver.unmanage_snapshot(snapshot_nfs)
|
||||||
|
self.assertTrue(self.mock_log.info.called)
|
||||||
|
@ -466,6 +466,10 @@ X Allow Change & Read Unix user\\1090
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
HNAS_RESULT_check_snap_error = """ \
|
||||||
|
path-to-object-number/FS-TestCG: Unable to locate component: share1
|
||||||
|
path-to-object-number/FS-TestCG: Failed to resolve object number"""
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
class HNASSSHTestCase(test.TestCase):
|
class HNASSSHTestCase(test.TestCase):
|
||||||
@ -880,6 +884,46 @@ class HNASSSHTestCase(test.TestCase):
|
|||||||
self._driver_ssh._locked_selectfs.assert_called_with(
|
self._driver_ssh._locked_selectfs.assert_called_with(
|
||||||
*locked_selectfs_args)
|
*locked_selectfs_args)
|
||||||
|
|
||||||
|
def test_check_snapshot(self):
|
||||||
|
path = ("/snapshots/" + self.snapshot['share_id'] + "/" +
|
||||||
|
self.snapshot['id'])
|
||||||
|
check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
|
||||||
|
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, '_execute')
|
||||||
|
|
||||||
|
out = self._driver_ssh.check_snapshot(path)
|
||||||
|
|
||||||
|
self.assertTrue(out)
|
||||||
|
self._driver_ssh._execute.assert_called_with(check_snap_args)
|
||||||
|
|
||||||
|
def test_check_inexistent_snapshot(self):
|
||||||
|
path = "/path/snap1/snapshot07-08-2016"
|
||||||
|
|
||||||
|
check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
|
||||||
|
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, '_execute',
|
||||||
|
mock.Mock(side_effect=putils.ProcessExecutionError(
|
||||||
|
stdout=HNAS_RESULT_check_snap_error)))
|
||||||
|
|
||||||
|
out = self._driver_ssh.check_snapshot(path)
|
||||||
|
|
||||||
|
self.assertFalse(out)
|
||||||
|
self._driver_ssh._execute.assert_called_with(check_snap_args)
|
||||||
|
|
||||||
|
def test_check_snapshot_error(self):
|
||||||
|
path = "/path/snap1/snapshot07-08-2016"
|
||||||
|
|
||||||
|
check_snap_args = ['path-to-object-number', '-f', self.fs_name, path]
|
||||||
|
|
||||||
|
self.mock_object(ssh.HNASSSHBackend, '_execute',
|
||||||
|
mock.Mock(side_effect=putils.ProcessExecutionError(
|
||||||
|
stdout="Internal Server Error.")))
|
||||||
|
|
||||||
|
self.assertRaises(putils.ProcessExecutionError,
|
||||||
|
self._driver_ssh.check_snapshot, path)
|
||||||
|
|
||||||
|
self._driver_ssh._execute.assert_called_with(check_snap_args)
|
||||||
|
|
||||||
def test_check_fs_mounted_true(self):
|
def test_check_fs_mounted_true(self):
|
||||||
self.mock_object(ssh.HNASSSHBackend, "_execute",
|
self.mock_object(ssh.HNASSSHBackend, "_execute",
|
||||||
mock.Mock(return_value=(HNAS_RESULT_df, '')))
|
mock.Mock(return_value=(HNAS_RESULT_df, '')))
|
||||||
|
@ -81,7 +81,10 @@ class ManageNFSSnapshotTest(base.BaseSharesAdminTest):
|
|||||||
snapshot['provider_location'],
|
snapshot['provider_location'],
|
||||||
name=name,
|
name=name,
|
||||||
description=description,
|
description=description,
|
||||||
driver_options={},
|
# Some drivers require additional parameters passed as driver
|
||||||
|
# options, as follows:
|
||||||
|
# - size: Hitachi HNAS Driver
|
||||||
|
driver_options={'size': snapshot['size']},
|
||||||
version=version,
|
version=version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Added manage/unmanage snapshot support to Hitachi HNAS Driver.
|
Loading…
Reference in New Issue
Block a user