NetApp: Refresh directory before waiting

After image cloning the NFS client cache needs to be refreshed.
This can be accomplished by touching the directory hosting the
cached image file.

See also: https://bugs.launchpad.net/nova/+bug/1617299
Co-Authored-By: Sebastian Schee <sebastian.schee@sap.com>
Co-Authored-By: Goutham Pacha Ravi <gouthampravi@gmail.com>

Closes-bug: #1679716
Change-Id: If392f41f65978721668b53cfab94393f074d24e9
(cherry picked from commit ff6acd62ec)
(cherry picked from commit cd3c1c2c37)
This commit is contained in:
Marc Koderer 2017-04-05 08:54:53 +02:00 committed by Goutham Pacha Ravi
parent 3b5a0fcef9
commit e4d719f646
3 changed files with 56 additions and 11 deletions

View File

@ -16,6 +16,9 @@
Mock unit tests for the NetApp cmode nfs storage driver Mock unit tests for the NetApp cmode nfs storage driver
""" """
import hashlib
import uuid
import ddt import ddt
import mock import mock
from os_brick.remotefs import remotefs as remotefs_brick from os_brick.remotefs import remotefs as remotefs_brick
@ -809,23 +812,35 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
def test__copy_from_img_service_qcow2_copyoffload_workflow_success( def test__copy_from_img_service_qcow2_copyoffload_workflow_success(
self, mock_exists, mock_qemu_img_info, mock_cvrt_image): self, mock_exists, mock_qemu_img_info, mock_cvrt_image):
drv = self.driver drv = self.driver
cinder_mount_point_base = '/opt/stack/data/cinder/mnt/'
# To get the cinder mount point directory, we use:
mount_dir = hashlib.md5(
'203.0.113.122:/cinder-flexvol1'.encode('utf-8')).hexdigest()
cinder_mount_point = cinder_mount_point_base + mount_dir
destination_copied_file = (
'/cinder-flexvol1/a155308c-0290-497b-b278-4cdd01de0253'
)
volume = {'id': 'vol_id', 'name': 'name', 'size': 1} volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
image_id = 'image_id' image_id = 'image_id'
context = object() context = object()
image_service = mock.Mock() image_service = mock.Mock()
image_service.get_location.return_value = ('nfs://ip1/openstack/img', image_service.get_location.return_value = (
None) 'nfs://203.0.113.122/glance-flexvol1', None)
image_service.show.return_value = {'size': 1, image_service.show.return_value = {'size': 1,
'disk_format': 'qcow2'} 'disk_format': 'qcow2'}
drv._check_get_nfs_path_segs =\ drv._check_get_nfs_path_segs = (
mock.Mock(return_value=('ip1', '/openstack')) mock.Mock(return_value=('203.0.113.122', '/openstack'))
)
drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1') drv._get_ip_verify_on_cluster = mock.Mock(return_value='203.0.113.122')
drv._get_host_ip = mock.Mock(return_value='ip2') drv._get_host_ip = mock.Mock(return_value='203.0.113.122')
drv._get_export_path = mock.Mock(return_value='/exp_path') drv._get_export_path = mock.Mock(
return_value='/cinder-flexvol1')
drv._get_provider_location = mock.Mock(return_value='share') drv._get_provider_location = mock.Mock(return_value='share')
drv._execute = mock.Mock() drv._execute = mock.Mock()
drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point') drv._execute_as_root = False
drv._get_mount_point_for_share = mock.Mock(
return_value=cinder_mount_point)
img_inf = mock.Mock() img_inf = mock.Mock()
img_inf.file_format = 'raw' img_inf.file_format = 'raw'
mock_qemu_img_info.return_value = img_inf mock_qemu_img_info.return_value = img_inf
@ -835,17 +850,33 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
drv._delete_file_at_path = mock.Mock() drv._delete_file_at_path = mock.Mock()
drv._clone_file_dst_exists = mock.Mock() drv._clone_file_dst_exists = mock.Mock()
drv._post_clone_image = mock.Mock() drv._post_clone_image = mock.Mock()
self.mock_object(uuid, 'uuid4', mock.Mock(
return_value='a155308c-0290-497b-b278-4cdd01de0253'))
retval = drv._copy_from_img_service( retval = drv._copy_from_img_service(
context, volume, image_service, image_id) context, volume, image_service, image_id)
self.assertIsNone(retval) self.assertIsNone(retval)
drv._get_ip_verify_on_cluster.assert_any_call('ip1') drv._get_ip_verify_on_cluster.assert_any_call('203.0.113.122')
drv._get_export_path.assert_called_with('vol_id') drv._get_export_path.assert_called_with('vol_id')
drv._check_share_can_hold_size.assert_called_with('share', 1) drv._check_share_can_hold_size.assert_called_with('share', 1)
drv._post_clone_image.assert_called_with(volume) drv._post_clone_image.assert_called_with(volume)
self.assertEqual(1, mock_cvrt_image.call_count) self.assertEqual(1, mock_cvrt_image.call_count)
self.assertEqual(1, drv._execute.call_count)
# _execute must be called once for copy-offload and again to touch
# the top directory to refresh cache
drv._execute.assert_has_calls(
[
mock.call(
'copyoffload_tool_path', '203.0.113.122',
'203.0.113.122', '/openstack/glance-flexvol1',
destination_copied_file, run_as_root=False,
check_exit_code=0
),
mock.call('touch', cinder_mount_point, run_as_root=False)
]
)
self.assertEqual(2, drv._execute.call_count)
self.assertEqual(2, drv._delete_file_at_path.call_count) self.assertEqual(2, drv._delete_file_at_path.call_count)
self.assertEqual(1, drv._clone_file_dst_exists.call_count) self.assertEqual(1, drv._clone_file_dst_exists.call_count)

View File

@ -645,6 +645,15 @@ class NetAppNfsDriver(driver.ManageableVD,
else: else:
return False return False
@utils.trace_method
def _touch_path_to_refresh(self, path):
try:
# Touching parent directory forces NFS client to flush its cache.
self._execute('touch', path, run_as_root=self._execute_as_root)
except processutils.ProcessExecutionError:
LOG.exception(_LE("Failed to touch path %s."), path)
@utils.trace_method
def _discover_file_till_timeout(self, path, timeout=75): def _discover_file_till_timeout(self, path, timeout=75):
"""Checks if file size at path is equal to size.""" """Checks if file size at path is equal to size."""
# Sometimes nfs takes time to discover file # Sometimes nfs takes time to discover file
@ -656,6 +665,9 @@ class NetAppNfsDriver(driver.ManageableVD,
# retries to ensure that this cache has refreshed. # retries to ensure that this cache has refreshed.
retry_seconds = timeout retry_seconds = timeout
sleep_interval = 2 sleep_interval = 2
base_path = os.path.dirname(path)
self._touch_path_to_refresh(base_path)
while True: while True:
if os.path.exists(path): if os.path.exists(path):
return True return True
@ -666,6 +678,7 @@ class NetAppNfsDriver(driver.ManageableVD,
else: else:
time.sleep(sleep_interval) time.sleep(sleep_interval)
retry_seconds -= sleep_interval retry_seconds -= sleep_interval
self._touch_path_to_refresh(base_path)
def _is_cloneable_share(self, image_location): def _is_cloneable_share(self, image_location):
"""Finds if the image at location is cloneable.""" """Finds if the image at location is cloneable."""

View File

@ -119,8 +119,9 @@ rm: CommandFilter, rm, root
# cinder/volume/drivers/remotefs.py # cinder/volume/drivers/remotefs.py
mkdir: CommandFilter, mkdir, root mkdir: CommandFilter, mkdir, root
# cinder/volume/drivers/netapp/nfs.py: # cinder/volume/drivers/netapp/dataontap/nfs_base.py:
netapp_nfs_find: RegExpFilter, find, root, find, ^[/]*([^/\0]+(/+)?)*$, -maxdepth, \d+, -name, img-cache.*, -amin, \+\d+ netapp_nfs_find: RegExpFilter, find, root, find, ^[/]*([^/\0]+(/+)?)*$, -maxdepth, \d+, -name, img-cache.*, -amin, \+\d+
netapp_nfs_touch: CommandFilter, touch, root
# cinder/volume/drivers/glusterfs.py # cinder/volume/drivers/glusterfs.py
chgrp: CommandFilter, chgrp, root chgrp: CommandFilter, chgrp, root