Fix HDS HNAS errors caused by incorrect IDs

When attempting to allow_access on a managed share, it fails
because the proper share ID is not retrieved from private storage
prior to attempting to validate that the share exists in the
backend. The same happens when trying to create a share from a
snapshot created from a managed share, the proper share ID is
not retrieved from private storage. While we are dealing with
two possible different IDs it is important to properly display
the API share ID in log messages so it can be matched to the
share instances ID, and not all log messages are accurately doing
so.

This change addresses this by retrieving the ID from
private storage first for update_access and
create_share_from_snapshot operations. The proper unit test changes
included in this patch required a refactor of several IDs to ensure
this problem is addressed in unit tests, thus it made sense to
address several bugs caused by the same problem, having the same fix
and requiring modifications to the same lines of code.

Closes-bug: #1581541
Closes-bug: #1584179
Closes-bug: #1583785
Change-Id: I8cdb1a8a72a4ac7e710f57e3c288d48cd2adf0dd
This commit is contained in:
Rodrigo Barbieri 2016-05-13 10:23:46 -03:00
parent ef2a6a8ac6
commit 081fc4860b
3 changed files with 156 additions and 114 deletions

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_config import cfg
from oslo_log import log
from oslo_utils import excutils
@ -151,13 +153,14 @@ class HDSHNASDriver(driver.ShareDriver):
Not used by this driver.
"""
hnas_share_id = self._get_hnas_share_id(share['id'])
try:
self._ensure_share(share['id'])
self._ensure_share(hnas_share_id)
except exception.HNASItemNotFoundException:
raise exception.ShareResourceNotFound(share_id=share['id'])
host_list = []
share_id = self._get_hnas_share_id(share['id'])
for rule in access_rules:
if rule['access_type'].lower() != 'ip':
@ -172,13 +175,13 @@ class HDSHNASDriver(driver.ShareDriver):
host_list.append(rule['access_to'] + '(' +
rule['access_level'] + ')')
self.hnas.update_access_rule(share_id, host_list)
self.hnas.update_access_rule(hnas_share_id, host_list)
if host_list:
LOG.debug("Share %(share)s has the rules: %(rules)s",
{'share': share_id, 'rules': ', '.join(host_list)})
{'share': share['id'], 'rules': ', '.join(host_list)})
else:
LOG.debug("Share %(share)s has no rules.", {'share': share_id})
LOG.debug("Share %(share)s has no rules.", {'share': share['id']})
def create_share(self, context, share, share_server=None):
"""Creates share.
@ -212,12 +215,15 @@ class HDSHNASDriver(driver.ShareDriver):
:param share_server: Data structure with share server information.
Not used by this driver.
"""
share_id = self._get_hnas_share_id(share['id'])
hnas_share_id = self._get_hnas_share_id(share['id'])
LOG.debug("Deleting share in HNAS: %(shr)s.",
{'shr': share['id']})
self._delete_share(share_id)
self._delete_share(hnas_share_id)
LOG.debug("Export and share successfully deleted: %(shr)s.",
{'shr': share['id']})
def create_snapshot(self, context, snapshot, share_server=None):
"""Creates snapshot.
@ -227,13 +233,13 @@ class HDSHNASDriver(driver.ShareDriver):
:param share_server: Data structure with share server information.
Not used by this driver.
"""
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 "
"id %(ss_id)s.", {'ss_sid': snapshot['share_id'],
'ss_id': snapshot['id']})
self._create_snapshot(share_id, snapshot['id'])
self._create_snapshot(hnas_share_id, snapshot['id'])
LOG.info(_LI("Snapshot %(id)s successfully created."),
{'id': snapshot['id']})
@ -245,13 +251,13 @@ class HDSHNASDriver(driver.ShareDriver):
:param share_server: Data structure with share server information.
Not used by this driver.
"""
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 %(ss_sid)s will be deleted. The related "
"share ID is %(ss_id)s.",
{'ss_sid': snapshot['share_id'], 'ss_id': snapshot['id']})
self._delete_snapshot(share_id, snapshot['id'])
self._delete_snapshot(hnas_share_id, snapshot['id'])
LOG.info(_LI("Snapshot %(id)s successfully deleted."),
{'id': snapshot['id']})
@ -271,11 +277,15 @@ class HDSHNASDriver(driver.ShareDriver):
LOG.debug("Creating a new share from snapshot: %(ss_id)s.",
{'ss_id': snapshot['id']})
path = self._create_share_from_snapshot(share, snapshot)
hnas_src_share_id = self._get_hnas_share_id(snapshot['share_id'])
path = self._create_share_from_snapshot(share, hnas_src_share_id,
snapshot)
uri = self.hnas_evs_ip + ":" + path
LOG.debug("Share created successfully on path: %(uri)s.",
{'uri': uri})
LOG.debug("Share %(share)s created successfully on path: %(uri)s.",
{'uri': uri,
'share': share['id']})
return uri
def ensure_share(self, context, share, share_server=None):
@ -291,9 +301,9 @@ class HDSHNASDriver(driver.ShareDriver):
LOG.debug("Ensuring share in HNAS: %(shr)s.",
{'shr': share['id']})
share_id = self._get_hnas_share_id(share['id'])
hnas_share_id = self._get_hnas_share_id(share['id'])
path = self._ensure_share(share_id)
path = self._ensure_share(hnas_share_id)
export = self.hnas_evs_ip + ":" + path
export_list = [export]
@ -310,12 +320,12 @@ class HDSHNASDriver(driver.ShareDriver):
:param share_server: Data structure with share server information.
Not used by this driver.
"""
share_id = self._get_hnas_share_id(share['id'])
hnas_share_id = self._get_hnas_share_id(share['id'])
LOG.debug("Expanding share in HNAS: %(shr_id)s.",
{'shr_id': share['id']})
self._extend_share(share_id, share['size'], new_size)
self._extend_share(hnas_share_id, share, new_size)
LOG.info(_LI("Share %(shr_id)s successfully extended to "
"%(shr_size)s."),
{'shr_id': share['id'],
@ -367,10 +377,12 @@ class HDSHNASDriver(driver.ShareDriver):
:returns: Returns a dict with size of share managed
and its location (your path in file-system).
"""
share_id = self._get_hnas_share_id(share['id'])
hnas_share_id = self._get_hnas_share_id(share['id'])
if share_id != share['id']:
msg = _("Share ID %s already exists, cannot manage.") % share_id
# Make sure returned value is the same as provided,
# confirming it does not exist.
if hnas_share_id != share['id']:
msg = _("Share ID %s already exists, cannot manage.") % share['id']
raise exception.HNASBackendException(msg=msg)
LOG.info(_LI("Share %(shr_path)s will be managed with ID %(shr_id)s."),
@ -382,7 +394,7 @@ class HDSHNASDriver(driver.ShareDriver):
if len(old_path) == 3:
evs_ip = old_path_info[0]
share_id = old_path[2]
hnas_share_id = old_path[2]
else:
msg = _("Incorrect path. It should have the following format: "
"IP:/shares/share_id.")
@ -398,9 +410,18 @@ class HDSHNASDriver(driver.ShareDriver):
"not configured.") % {'shr': share['host']}
raise exception.ShareBackendException(msg=msg)
output = self._manage_existing(share_id)
output = self._manage_existing(share['id'], hnas_share_id)
self.private_storage.update(
share['id'], {'hnas_id': share_id})
share['id'], {'hnas_id': hnas_share_id})
LOG.debug("HNAS ID %(hnas_id)s has been saved to private storage for "
"Share ID %(share_id)s", {'hnas_id': hnas_share_id,
'share_id': share['id']})
LOG.info(_LI("Share %(shr_path)s was successfully managed with ID "
"%(shr_id)s."),
{'shr_path': share['export_locations'][0]['path'],
'shr_id': share['id']})
return output
@ -428,12 +449,12 @@ class HDSHNASDriver(driver.ShareDriver):
:param share_server: Data structure with share server information.
Not used by this driver.
"""
share_id = self._get_hnas_share_id(share['id'])
hnas_share_id = self._get_hnas_share_id(share['id'])
LOG.debug("Shrinking share in HNAS: %(shr_id)s.",
{'shr_id': share['id']})
self._shrink_share(share_id, new_size)
self._shrink_share(hnas_share_id, share, new_size)
LOG.info(_LI("Share %(shr_id)s successfully shrunk to "
"%(shr_size)sG."),
{'shr_id': share['id'],
@ -444,18 +465,23 @@ class HDSHNASDriver(driver.ShareDriver):
if hnas_id is None:
hnas_id = share_id
LOG.debug("Share ID is %(shr_id)s and respective HNAS ID "
"is %(hnas_id)s.", {'shr_id': share_id,
'hnas_id': hnas_id})
return hnas_id
def _create_share(self, share_id, share_size):
"""Creates share.
Creates a virtual-volume, adds a quota limit and exports it.
:param share_id: ID of share that will be created.
:param share_id: manila's database ID of share that will be created.
:param share_size: Size limit of share.
:returns: Returns a path of /shares/share_id if the export was
created successfully.
"""
path = '/shares/' + share_id
path = os.path.join('/shares', share_id)
self._check_fs_mounted()
@ -484,112 +510,113 @@ class HDSHNASDriver(driver.ShareDriver):
msg = _("Filesystem %s is not mounted.") % self.fs_name
raise exception.HNASBackendException(msg=msg)
def _ensure_share(self, share_id):
def _ensure_share(self, hnas_share_id):
"""Ensure that share is exported.
:param share_id: ID of share that will be checked.
:param hnas_share_id: HNAS ID of share that will be checked.
:returns: Returns a path of /shares/share_id if the export is ok.
"""
path = '/shares/' + share_id
path = os.path.join('/shares', hnas_share_id)
self._check_fs_mounted()
self.hnas.check_vvol(share_id)
self.hnas.check_quota(share_id)
self.hnas.check_export(share_id)
self.hnas.check_vvol(hnas_share_id)
self.hnas.check_quota(hnas_share_id)
self.hnas.check_export(hnas_share_id)
return path
def _shrink_share(self, share_id, new_size):
def _shrink_share(self, hnas_share_id, share, new_size):
"""Shrinks a share to new size.
:param share_id: ID of share that will be shrunk.
:param hnas_share_id: HNAS ID of share that will be shrunk.
:param share: model of share that will be shrunk.
:param new_size: New size of share after shrink operation.
"""
self._ensure_share(share_id)
self._ensure_share(hnas_share_id)
usage = self.hnas.get_share_usage(share_id)
usage = self.hnas.get_share_usage(hnas_share_id)
LOG.debug("Usage space in share %(share)s: %(usage)sG",
{'share': share_id, 'usage': usage})
{'share': share['id'], 'usage': usage})
if new_size > usage:
self.hnas.modify_quota(share_id, new_size)
self.hnas.modify_quota(hnas_share_id, new_size)
else:
raise exception.ShareShrinkingPossibleDataLoss(share_id=share_id)
raise exception.ShareShrinkingPossibleDataLoss(
share_id=share['id'])
def _extend_share(self, share_id, old_size, new_size):
def _extend_share(self, hnas_share_id, share, new_size):
"""Extends a share to new size.
:param share_id: ID of share that will be extended.
:param old_size: Current size of share that will be extended.
:param hnas_share_id: HNAS ID of share that will be extended.
:param share: model of share that will be extended.
:param new_size: New size of share after extend operation.
"""
self._ensure_share(share_id)
self._ensure_share(hnas_share_id)
old_size = share['size']
total, available_space = self.hnas.get_stats()
LOG.debug("Available space in filesystem: %(space)sG.",
{'space': available_space})
if (new_size - old_size) < available_space:
self.hnas.modify_quota(share_id, new_size)
self.hnas.modify_quota(hnas_share_id, new_size)
else:
msg = (_("Share %s cannot be extended due to insufficient space.")
% share_id)
% share['id'])
raise exception.HNASBackendException(msg=msg)
def _delete_share(self, share_id):
def _delete_share(self, hnas_share_id):
"""Deletes share.
It uses tree-delete-job-submit to format and delete virtual-volumes.
Quota is deleted with virtual-volume.
:param share_id: ID of share that will be deleted.
:param hnas_share_id: HNAS ID of share that will be deleted.
"""
self._check_fs_mounted()
self.hnas.nfs_export_del(share_id)
self.hnas.vvol_delete(share_id)
self.hnas.nfs_export_del(hnas_share_id)
self.hnas.vvol_delete(hnas_share_id)
LOG.debug("Export and share successfully deleted: %(shr)s on Manila.",
{'shr': share_id})
def _manage_existing(self, share_id):
def _manage_existing(self, share_id, hnas_share_id):
"""Manages a share that exists on backend.
:param share_id: ID of share that will be managed.
:param share_id: manila's database ID of share that will be managed.
:param hnas_share_id: HNAS ID of share that will be managed.
:returns: Returns a dict with size of share managed
and its location (your path in file-system).
"""
self._ensure_share(share_id)
self._ensure_share(hnas_share_id)
share_size = self.hnas.get_share_quota(share_id)
share_size = self.hnas.get_share_quota(hnas_share_id)
if share_size is None:
msg = (_("The share %s trying to be managed does not have a "
"quota limit, please set it before manage.") % share_id)
raise exception.ManageInvalidShare(reason=msg)
path = self.hnas_evs_ip + ':/shares/' + share_id
path = self.hnas_evs_ip + os.path.join(':/shares', hnas_share_id)
return {'size': share_size, 'export_locations': [path]}
def _create_snapshot(self, share_id, snapshot_id):
def _create_snapshot(self, hnas_share_id, snapshot_id):
"""Creates a snapshot of share.
It copies the directory and all files to a new directory inside
/snapshots/share_id/.
:param share_id: ID of share for snapshot.
:param hnas_share_id: HNAS ID of share for snapshot.
:param snapshot_id: ID of new snapshot.
"""
self._ensure_share(share_id)
self._ensure_share(hnas_share_id)
saved_list = self.hnas.get_host_list(share_id)
saved_list = self.hnas.get_host_list(hnas_share_id)
new_list = []
for access in saved_list:
new_list.append(access.replace('(rw)', '(ro)'))
self.hnas.update_access_rule(share_id, new_list)
self.hnas.update_access_rule(hnas_share_id, new_list)
src_path = '/shares/' + share_id
dest_path = '/snapshots/' + share_id + '/' + snapshot_id
src_path = os.path.join('/shares', hnas_share_id)
dest_path = os.path.join('/snapshots', hnas_share_id, snapshot_id)
try:
self.hnas.tree_clone(src_path, dest_path)
except exception.HNASNothingToCloneException:
@ -597,35 +624,37 @@ class HDSHNASDriver(driver.ShareDriver):
"directory."))
self.hnas.create_directory(dest_path)
finally:
self.hnas.update_access_rule(share_id, saved_list)
self.hnas.update_access_rule(hnas_share_id, saved_list)
def _delete_snapshot(self, share_id, snapshot_id):
def _delete_snapshot(self, hnas_share_id, snapshot_id):
"""Deletes snapshot.
It receives the share_id only to mount the path for snapshot.
:param share_id: ID of share that snapshot was created.
It receives the hnas_share_id only to join the path for snapshot.
:param hnas_share_id: HNAS ID of share from which snapshot was taken.
:param snapshot_id: ID of snapshot.
"""
self._check_fs_mounted()
path = '/snapshots/' + share_id + '/' + snapshot_id
path = os.path.join('/snapshots', hnas_share_id, snapshot_id)
self.hnas.tree_delete(path)
path = '/snapshots/' + share_id
path = os.path.join('/snapshots', hnas_share_id)
self.hnas.delete_directory(path)
def _create_share_from_snapshot(self, share, snapshot):
def _create_share_from_snapshot(self, share, src_hnas_share_id, snapshot):
"""Creates a new share from snapshot.
It copies everything from snapshot directory to a new vvol,
set a quota limit for it and export.
:param share: a dict from new share.
:param src_hnas_share_id: HNAS ID of share from which snapshot was
taken.
:param snapshot: a dict from snapshot that will be copied to
new share.
:returns: Returns the path for new share.
"""
dest_path = '/shares/' + share['id']
src_path = '/snapshots/' + snapshot['share_id'] + '/' + snapshot['id']
dest_path = os.path.join('/shares', share['id'])
src_path = os.path.join('/snapshots', src_hnas_share_id,
snapshot['id'])
# Before copying everything to new vvol, we need to create it,
# because we only can transform an empty directory into a vvol.

View File

@ -153,16 +153,18 @@ class HDSHNASTestCase(test.TestCase):
}
access_list = [access1, access2]
self.mock_object(self._driver, '_get_hnas_share_id',
mock.Mock(return_value='hnas_id'))
self.mock_object(self._driver, '_ensure_share')
self.mock_object(ssh.HNASSSHBackend, "update_access_rule",
mock.Mock())
self._driver.update_access('context', share, access_list, [], [])
ssh.HNASSSHBackend.update_access_rule.assert_called_once_with(
share['id'], [access1['access_to'] + '('
+ access1['access_level'] + ',norootsquash)',
access2['access_to'] + '('
+ access2['access_level'] + ')'])
'hnas_id', [access1['access_to'] + '('
+ access1['access_level'] + ',norootsquash)',
access2['access_to'] + '('
+ access2['access_level'] + ')'])
self.assertTrue(self.mock_log.debug.called)
def test_update_access_ip_exception(self):
@ -249,7 +251,7 @@ class HDSHNASTestCase(test.TestCase):
def test_delete_share(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_check_fs_mounted",
mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "nfs_export_del", mock.Mock())
@ -258,13 +260,13 @@ class HDSHNASTestCase(test.TestCase):
self._driver.delete_share('context', share)
self.assertTrue(self.mock_log.debug.called)
ssh.HNASSSHBackend.nfs_export_del.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.vvol_delete.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.nfs_export_del.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.vvol_delete.assert_called_once_with('hnas_id')
def test_create_snapshot(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share")
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(ssh.HNASSSHBackend, "get_host_list", mock.Mock(
return_value=['172.24.44.200(rw)']))
self.mock_object(ssh.HNASSSHBackend, "update_access_rule", mock.Mock())
@ -272,19 +274,19 @@ class HDSHNASTestCase(test.TestCase):
self._driver.create_snapshot('context', snapshot)
ssh.HNASSSHBackend.get_host_list.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.get_host_list.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.update_access_rule.assert_any_call(
share['id'], ['172.24.44.200(ro)'])
'hnas_id', ['172.24.44.200(ro)'])
ssh.HNASSSHBackend.update_access_rule.assert_any_call(
share['id'], ['172.24.44.200(rw)'])
'hnas_id', ['172.24.44.200(rw)'])
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
'/shares/' + share['id'], '/snapshots/' + share['id'] + '/' +
snapshot['id'])
'/shares/' + 'hnas_id', '/snapshots/' + 'hnas_id' + '/' +
snapshot['id'])
def test_create_snapshot_first_snapshot(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share")
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(ssh.HNASSSHBackend, "get_host_list", mock.Mock(
return_value=['172.24.44.200(rw)']))
self.mock_object(ssh.HNASSSHBackend, "update_access_rule", mock.Mock())
@ -295,17 +297,17 @@ class HDSHNASTestCase(test.TestCase):
self._driver.create_snapshot('context', snapshot)
self.assertTrue(self.mock_log.warning.called)
ssh.HNASSSHBackend.get_host_list.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.get_host_list.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.update_access_rule.assert_any_call(
share['id'], ['172.24.44.200(ro)'])
'hnas_id', ['172.24.44.200(ro)'])
ssh.HNASSSHBackend.update_access_rule.assert_any_call(
share['id'], ['172.24.44.200(rw)'])
'hnas_id', ['172.24.44.200(rw)'])
ssh.HNASSSHBackend.create_directory.assert_called_once_with(
'/snapshots/' + share['id'] + '/' + snapshot['id'])
'/snapshots/' + 'hnas_id' + '/' + snapshot['id'])
def test_delete_snapshot(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_check_fs_mounted")
self.mock_object(ssh.HNASSSHBackend, "tree_delete", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "delete_directory", mock.Mock())
@ -316,13 +318,13 @@ class HDSHNASTestCase(test.TestCase):
self.assertTrue(self.mock_log.info.called)
hds_hnas.HDSHNASDriver._check_fs_mounted.assert_called_once_with()
ssh.HNASSSHBackend.tree_delete.assert_called_once_with(
'/snapshots/' + share['id'] + '/' + snapshot['id'])
'/snapshots/' + 'hnas_id' + '/' + snapshot['id'])
ssh.HNASSSHBackend.delete_directory.assert_called_once_with(
'/snapshots/' + share['id'])
'/snapshots/' + 'hnas_id')
def test_ensure_share(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_check_fs_mounted",
mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "check_vvol", mock.Mock())
@ -331,14 +333,14 @@ class HDSHNASTestCase(test.TestCase):
result = self._driver.ensure_share('context', share)
self.assertEqual(['172.24.44.10:/shares/' + share['id']], result)
ssh.HNASSSHBackend.check_vvol.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.check_quota.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.check_export.assert_called_once_with(share['id'])
self.assertEqual(['172.24.44.10:/shares/' + 'hnas_id'], result)
ssh.HNASSSHBackend.check_vvol.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.check_quota.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.check_export.assert_called_once_with('hnas_id')
def test_shrink_share(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "get_share_usage", mock.Mock(
return_value=10))
@ -346,24 +348,23 @@ class HDSHNASTestCase(test.TestCase):
self._driver.shrink_share(share, 11)
ssh.HNASSSHBackend.get_share_usage.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.modify_quota.assert_called_once_with(share['id'],
11)
ssh.HNASSSHBackend.get_share_usage.assert_called_once_with('hnas_id')
ssh.HNASSSHBackend.modify_quota.assert_called_once_with('hnas_id', 11)
def test_shrink_share_new_size_lower_than_usage(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "get_share_usage", mock.Mock(
return_value=10))
self.assertRaises(exception.ShareShrinkingPossibleDataLoss,
self._driver.shrink_share, share, 9)
ssh.HNASSSHBackend.get_share_usage.assert_called_once_with(share['id'])
ssh.HNASSSHBackend.get_share_usage.assert_called_once_with('hnas_id')
def test_extend_share(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "get_stats", mock.Mock(
return_value=(500, 200)))
@ -372,12 +373,11 @@ class HDSHNASTestCase(test.TestCase):
self._driver.extend_share(share, 150)
ssh.HNASSSHBackend.get_stats.assert_called_once_with()
ssh.HNASSSHBackend.modify_quota.assert_called_once_with(share['id'],
150)
ssh.HNASSSHBackend.modify_quota.assert_called_once_with('hnas_id', 150)
def test_extend_share_with_no_available_space_in_fs(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value=share['id']))
mock.Mock(return_value='hnas_id'))
self.mock_object(hds_hnas.HDSHNASDriver, "_ensure_share", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "get_stats", mock.Mock(
return_value=(500, 200)))
@ -450,6 +450,8 @@ class HDSHNASTestCase(test.TestCase):
def test_create_share_from_snapshot(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_check_fs_mounted",
mock.Mock())
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value='hnas_id'))
self.mock_object(ssh.HNASSSHBackend, "vvol_create", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "quota_add", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock())
@ -463,13 +465,15 @@ class HDSHNASTestCase(test.TestCase):
ssh.HNASSSHBackend.quota_add.assert_called_once_with(share['id'],
share['size'])
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
'/snapshots/' + snapshot['share_id'] + '/' + snapshot['id'],
'/snapshots/' + 'hnas_id' + '/' + snapshot['id'],
'/shares/' + share['id'])
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(share['id'])
def test_create_share_from_snapshot_empty_snapshot(self):
self.mock_object(hds_hnas.HDSHNASDriver, "_check_fs_mounted",
mock.Mock())
self.mock_object(hds_hnas.HDSHNASDriver, "_get_hnas_share_id",
mock.Mock(return_value='hnas_id'))
self.mock_object(ssh.HNASSSHBackend, "vvol_create", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "quota_add", mock.Mock())
self.mock_object(ssh.HNASSSHBackend, "tree_clone", mock.Mock(
@ -485,7 +489,7 @@ class HDSHNASTestCase(test.TestCase):
ssh.HNASSSHBackend.quota_add.assert_called_once_with(share['id'],
share['size'])
ssh.HNASSSHBackend.tree_clone.assert_called_once_with(
'/snapshots/' + snapshot['share_id'] + '/' + snapshot['id'],
'/snapshots/' + 'hnas_id' + '/' + snapshot['id'],
'/shares/' + share['id'])
ssh.HNASSSHBackend.nfs_export_add.assert_called_once_with(share['id'])

View File

@ -0,0 +1,9 @@
---
fixes:
- Fixed error when allowing access to a managed share in
HDS HNAS driver.
- Fixed error when attempting to create a new share from
a snapshot taken from a managed share in HDS HNAS
driver.
- Fixed ID inconsistencies in log when handling managed
shares in HDS HNAS driver.