NetApp ONTAP: Copy offload bugfix

When 'NetApp NFS Copy Offload' tool is configured to download Glance
images, these images are downloaded twice because the tool is doing
the job after Cinder has already done it.
This patch fixes the bug by executing the copy offload tool inside the
clone_image function instead of using the copy_image_to_volume.

Closes-bug: #1632333

Change-Id: I5c6ad150543213acfd0c78dbbdb1dc1584d22d26
This commit is contained in:
Adriano Rosso 2017-11-08 15:51:36 -02:00 committed by Goutham Pacha Ravi
parent d2782c0389
commit c27173bad6
4 changed files with 136 additions and 168 deletions

View File

@ -763,26 +763,25 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.driver.zapi_client.clone_file.assert_called_once_with(
'nfsvol', 'vol', 'clone', None, is_snapshot=True)
def test__copy_from_img_service_copyoffload_nonexistent_binary_path(self):
def test_copy_from_img_service_copyoffload_nonexistent_binary_path(self):
self.mock_object(nfs_cmode.LOG, 'debug')
drv = self.driver
context = object()
volume = {'id': 'vol_id', 'name': 'name'}
volume = {'id': 'vol_id', 'name': 'name',
'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'}
image_service = mock.Mock()
image_service.get_location.return_value = (mock.Mock(), mock.Mock())
image_service.show.return_value = {'size': 0}
image_id = 'image_id'
drv._client = mock.Mock()
drv._client.get_api_version = mock.Mock(return_value=(1, 20))
drv._find_image_in_cache = mock.Mock(return_value=[])
nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
return_value=[])
drv._construct_image_nfs_url = mock.Mock(return_value=["nfs://1"])
drv._check_get_nfs_path_segs = mock.Mock(
return_value=("test:test", "dr"))
drv._get_ip_verify_on_cluster = mock.Mock(return_value="192.128.1.1")
drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point')
drv._get_host_ip = mock.Mock()
drv._get_provider_location = mock.Mock()
drv._get_export_path = mock.Mock(return_value="dr")
drv._check_share_can_hold_size = mock.Mock()
# Raise error as if the copyoffload file can not be found
drv._clone_file_dst_exists = mock.Mock(side_effect=OSError())
@ -795,10 +794,11 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
drv._discover_file_till_timeout.assert_not_called()
@mock.patch.object(image_utils, 'qemu_img_info')
def test__copy_from_img_service_raw_copyoffload_workflow_success(
def test_copy_from_img_service_raw_copyoffload_workflow_success(
self, mock_qemu_img_info):
drv = self.driver
volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
volume = {'id': 'vol_id', 'name': 'name', 'size': 1,
'host': 'openstack@nfscmode#ip1:/mnt_point'}
image_id = 'image_id'
context = object()
image_service = mock.Mock()
@ -827,17 +827,16 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
retval = drv._copy_from_img_service(
context, volume, image_service, image_id)
self.assertIsNone(retval)
self.assertTrue(retval)
drv._get_ip_verify_on_cluster.assert_any_call('ip1')
drv._get_export_path.assert_called_with('vol_id')
drv._check_share_can_hold_size.assert_called_with('share', 1)
drv._post_clone_image.assert_called_with(volume)
drv._check_share_can_hold_size.assert_called_with(
'ip1:/mnt_point', 1)
self.assertEqual(1, drv._execute.call_count)
@mock.patch.object(image_utils, 'convert_image')
@mock.patch.object(image_utils, 'qemu_img_info')
@mock.patch('os.path.exists')
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):
drv = self.driver
cinder_mount_point_base = '/opt/stack/data/cinder/mnt/'
@ -848,7 +847,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
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,
'host': 'openstack@nfscmode#203.0.113.122:/cinder-flexvol1'}
image_id = 'image_id'
context = object()
image_service = mock.Mock()
@ -861,10 +861,6 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
)
drv._get_ip_verify_on_cluster = mock.Mock(return_value='203.0.113.122')
drv._get_host_ip = mock.Mock(return_value='203.0.113.122')
drv._get_export_path = mock.Mock(
return_value='/cinder-flexvol1')
drv._get_provider_location = mock.Mock(return_value='share')
drv._execute = mock.Mock()
drv._execute_as_root = False
drv._get_mount_point_for_share = mock.Mock(
@ -884,12 +880,10 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
retval = drv._copy_from_img_service(
context, volume, image_service, image_id)
self.assertIsNone(retval)
self.assertTrue(retval)
drv._get_ip_verify_on_cluster.assert_any_call('203.0.113.122')
drv._get_export_path.assert_called_with('vol_id')
drv._check_share_can_hold_size.assert_called_with('share', 1)
drv._post_clone_image.assert_called_with(volume)
self.assertEqual(1, mock_cvrt_image.call_count)
drv._check_share_can_hold_size.assert_called_with(
'203.0.113.122:/cinder-flexvol1', 1)
# _execute must be called once for copy-offload and again to touch
# the top directory to refresh cache
@ -908,30 +902,25 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.assertEqual(2, drv._delete_file_at_path.call_count)
self.assertEqual(1, drv._clone_file_dst_exists.call_count)
def test__copy_from_cache_copyoffload_success(self):
def test_copy_from_cache_copyoffload_success(self):
drv = self.driver
volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
volume = {'id': 'vol_id', 'name': 'name', 'size': 1,
'host': 'openstack@nfscmode#192.128.1.1:/exp_path'}
image_id = 'image_id'
cache_result = [('ip1:/openstack', 'img-cache-imgid')]
drv._get_ip_verify_on_cluster = mock.Mock(return_value='ip1')
drv._get_host_ip = mock.Mock(return_value='ip2')
drv._get_export_path = mock.Mock(return_value='/exp_path')
drv._execute = mock.Mock()
drv._register_image_in_cache = mock.Mock()
drv._get_provider_location = mock.Mock(return_value='/share')
drv._post_clone_image = mock.Mock()
copied = drv._copy_from_cache(volume, image_id, cache_result)
self.assertTrue(copied)
drv._get_ip_verify_on_cluster.assert_any_call('ip1')
drv._get_export_path.assert_called_with('vol_id')
drv._execute.assert_called_once_with(
'copyoffload_tool_path', 'ip1', 'ip1',
'/openstack/img-cache-imgid', '/exp_path/name',
run_as_root=False, check_exit_code=0)
drv._post_clone_image.assert_called_with(volume)
drv._get_provider_location.assert_called_with('vol_id')
def test_unmanage(self):
mock_get_info = self.mock_object(na_utils,
@ -1080,40 +1069,38 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
(local_share, 'img-cache-imgid'),
('ip3:/openstack', 'img-cache-imgid'),
]
self.driver._get_provider_location = mock.Mock(
return_value=local_share)
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
mock_extract_host.return_value = local_share
cache_copy, found_local_copy = self.driver._find_image_location(
cache_result, fake.VOLUME_ID)
cache_result, fake.VOLUME)
self.assertEqual(cache_result[2], cache_copy)
self.assertTrue(found_local_copy)
self.driver._get_provider_location.assert_called_once_with(
fake.VOLUME_ID)
def test_find_image_location_with_remote_copy(self):
cache_result = [('ip1:/openstack', 'img-cache-imgid')]
self.driver._get_provider_location = mock.Mock(return_value='/share')
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
mock_extract_host.return_value = '/share'
cache_copy, found_local_copy = self.driver._find_image_location(
cache_result, fake.VOLUME_ID)
cache_result, fake.VOLUME)
self.assertEqual(cache_result[0], cache_copy)
self.assertFalse(found_local_copy)
self.driver._get_provider_location.assert_called_once_with(
fake.VOLUME_ID)
def test_find_image_location_without_cache_copy(self):
cache_result = []
self.driver._get_provider_location = mock.Mock(return_value='/share')
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
mock_extract_host.return_value = '/share'
cache_copy, found_local_copy = self.driver._find_image_location(
cache_result, fake.VOLUME_ID)
cache_result, fake.VOLUME)
self.assertIsNone(cache_copy)
self.assertFalse(found_local_copy)
self.driver._get_provider_location.assert_called_once_with(
fake.VOLUME_ID)
def test_clone_file_dest_exists(self):
self.driver._get_vserver_and_exp_vol = mock.Mock(
@ -1146,8 +1133,8 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
def test_get_destination_ip_and_path(self):
self.driver._get_ip_verify_on_cluster = mock.Mock(
return_value=fake.SHARE_IP)
self.driver._get_host_ip = mock.Mock(return_value='host.ip')
self.driver._get_export_path = mock.Mock(return_value=fake.EXPORT_PATH)
mock_extract_host = self.mock_object(volume_utils, 'extract_host')
mock_extract_host.return_value = fake.NFS_SHARE
dest_ip, dest_path = self.driver._get_destination_ip_and_path(
fake.VOLUME)
@ -1156,98 +1143,85 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
assert_path = fake.EXPORT_PATH + '/' + fake.LUN_NAME
self.assertEqual(assert_path, dest_path)
self.driver._get_ip_verify_on_cluster.assert_called_once_with(
'host.ip')
self.driver._get_host_ip.assert_called_once_with(fake.VOLUME_ID)
self.driver._get_export_path.assert_called_once_with(fake.VOLUME_ID)
fake.SHARE_IP)
def test_copy_image_to_volume_copyoffload_non_cached_ssc_update(self):
mock_log = self.mock_object(nfs_cmode, 'LOG')
def test_clone_image_copyoffload_from_cache_success(self):
drv = self.driver
context = object()
volume = {'id': 'vol_id', 'name': 'name'}
volume = {'id': 'vol_id', 'name': 'name',
'host': 'openstack@nfscmode#192.128.1.1:/mnt_point'}
image_service = object()
image_location = 'img-loc'
image_id = 'image_id'
image_meta = {'id': image_id}
drv.zapi_client = mock.Mock()
drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
drv._copy_from_img_service = mock.Mock()
drv._get_provider_location = mock.Mock(return_value='share')
drv._get_vol_for_share = mock.Mock(return_value='vol')
retval = drv.copy_image_to_volume(
context, volume, image_service, image_id)
self.assertIsNone(retval)
drv._copy_from_img_service.assert_called_once_with(
context, volume, image_service, image_id)
self.assertEqual(1, mock_log.debug.call_count)
self.assertEqual(1, mock_log.info.call_count)
def test_copy_image_to_volume_copyoffload_from_cache_success(self):
mock_info_log = self.mock_object(nfs_cmode.LOG, 'info')
drv = self.driver
context = object()
volume = {'id': 'vol_id', 'name': 'name'}
image_service = object()
image_id = 'image_id'
drv.zapi_client = mock.Mock()
drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
nfs_base.NetAppNfsDriver.copy_image_to_volume = mock.Mock()
drv._get_provider_location = mock.Mock(return_value='share')
drv._get_vol_for_share = mock.Mock(return_value='vol')
drv._find_image_in_cache = mock.Mock(return_value=[('share', 'img')])
nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
return_value=[('share', 'img')])
nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
return_value=False)
drv._copy_from_cache = mock.Mock(return_value=True)
drv.copy_image_to_volume(context, volume, image_service, image_id)
drv.clone_image(context, volume, image_location, image_meta,
image_service)
drv._copy_from_cache.assert_called_once_with(
volume, image_id, [('share', 'img')])
self.assertEqual(1, mock_info_log.call_count)
def test_copy_image_to_volume_copyoffload_from_img_service(self):
def test_clone_image_copyoffload_from_img_service(self):
drv = self.driver
context = object()
volume = {'id': 'vol_id', 'name': 'name'}
volume = {'id': 'vol_id', 'name': 'name',
'host': 'openstack@nfscmode#192.128.1.1:/mnt_point',
'provider_location': '192.128.1.1:/mnt_point'}
image_service = object()
image_id = 'image_id'
image_meta = {'id': image_id}
image_location = 'img-loc'
drv.zapi_client = mock.Mock()
drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
nfs_base.NetAppNfsDriver.copy_image_to_volume = mock.Mock()
drv._get_provider_location = mock.Mock(return_value='share')
drv._get_vol_for_share = mock.Mock(return_value='vol')
drv._find_image_in_cache = mock.Mock(return_value=False)
drv._copy_from_img_service = mock.Mock()
nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
return_value=[])
nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
return_value=False)
nfs_base.NetAppNfsDriver._post_clone_image = mock.Mock(
return_value=True)
drv._copy_from_img_service = mock.Mock(return_value=True)
retval = drv.copy_image_to_volume(
context, volume, image_service, image_id)
retval = drv.clone_image(
context, volume, image_location, image_meta, image_service)
self.assertIsNone(retval)
self.assertEqual(retval, (
{'provider_location': '192.128.1.1:/mnt_point',
'bootable': True}, True))
drv._copy_from_img_service.assert_called_once_with(
context, volume, image_service, image_id)
def test_copy_image_to_volume_copyoffload_failure(self):
def test_clone_image_copyoffload_failure(self):
mock_log = self.mock_object(nfs_cmode, 'LOG')
drv = self.driver
context = object()
volume = {'id': 'vol_id', 'name': 'name'}
image_service = object()
image_id = 'image_id'
image_meta = {'id': image_id}
image_location = 'img-loc'
drv.zapi_client = mock.Mock()
drv.zapi_client.get_ontapi_version = mock.Mock(return_value=(1, 20))
nfs_base.NetAppNfsDriver._find_image_in_cache = mock.Mock(
return_value=[])
nfs_base.NetAppNfsDriver._direct_nfs_clone = mock.Mock(
return_value=False)
drv._copy_from_img_service = mock.Mock(side_effect=Exception())
nfs_base.NetAppNfsDriver.copy_image_to_volume = mock.Mock()
drv._get_provider_location = mock.Mock(return_value='share')
drv._get_vol_for_share = mock.Mock(return_value='vol')
retval = drv.copy_image_to_volume(
context, volume, image_service, image_id)
retval = drv.clone_image(
context, volume, image_location, image_meta, image_service)
self.assertIsNone(retval)
self.assertEqual(retval, ({'bootable': False,
'provider_location': None}, False))
drv._copy_from_img_service.assert_called_once_with(
context, volume, image_service, image_id)
nfs_base.NetAppNfsDriver.copy_image_to_volume. \
assert_called_once_with(context, volume, image_service, image_id)
mock_log.info.assert_not_called()
self.assertEqual(1, mock_log.exception.call_count)
def test_copy_from_remote_cache(self):
source_ip = '192.0.1.1'
@ -1289,7 +1263,6 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.assertTrue(copied)
self.driver._copy_from_remote_cache.assert_called_once_with(
fake.VOLUME, fake.IMAGE_FILE_ID, cache_result[0])
self.driver._post_clone_image.assert_called_once_with(fake.VOLUME)
def test_copy_from_cache_workflow_remote_location_no_copyoffload(self):
cache_result = [('ip1:/openstack', fake.IMAGE_FILE_ID),
@ -1327,7 +1300,6 @@ class NetAppCmodeNfsDriverTestCase(test.TestCase):
self.driver._clone_file_dst_exists.assert_called_once_with(
local_share, fake.IMAGE_FILE_ID, fake.VOLUME['name'],
dest_exists=True)
self.driver._post_clone_image.assert_called_once_with(fake.VOLUME)
def test_copy_from_cache_workflow_no_location(self):
cache_result = []

View File

@ -511,6 +511,14 @@ class NetAppNfsDriver(driver.ManageableVD,
LOG.warning('Exception during deleting %s', ex)
return False
def _copy_from_cache(self, volume, image_id, cache_result):
"""Try copying image file_name from cached file"""
raise NotImplementedError()
def _copy_from_img_service(self, context, volume, image_service,
image_id):
raise NotImplementedError()
def clone_image(self, context, volume,
image_location, image_meta,
image_service):
@ -528,14 +536,22 @@ class NetAppNfsDriver(driver.ManageableVD,
post_clone = False
extra_specs = na_utils.get_volume_extra_specs(volume)
major, minor = self.zapi_client.get_ontapi_version()
col_path = self.configuration.netapp_copyoffload_tool_path
try:
cache_result = self._find_image_in_cache(image_id)
if cache_result:
cloned = self._clone_from_cache(volume, image_id, cache_result)
cloned = self._copy_from_cache(volume, image_id, cache_result)
else:
cloned = self._direct_nfs_clone(volume, image_location,
image_id)
# Try to use the copy offload tool
if not cloned and col_path and major == 1 and minor >= 20:
cloned = self._copy_from_img_service(context, volume,
image_service, image_id)
if cloned:
self._do_qos_for_volume(volume, extra_specs)
post_clone = self._post_clone_image(volume)
@ -546,7 +562,8 @@ class NetAppNfsDriver(driver.ManageableVD,
{'image_id': image_id, 'msg': msg})
finally:
cloned = cloned and post_clone
share = volume['provider_location'] if cloned else None
share = (volume_utils.extract_host(volume['host'], level='pool')
if cloned else None)
bootable = True if cloned else False
return {'provider_location': share, 'bootable': bootable}, cloned
@ -583,7 +600,6 @@ class NetAppNfsDriver(driver.ManageableVD,
share = self._is_cloneable_share(loc)
if share and self._is_share_clone_compatible(volume, share):
LOG.debug('Share is cloneable %s', share)
volume['provider_location'] = share
(__, ___, img_file) = loc.rpartition('/')
dir_path = self._get_mount_point_for_share(share)
img_path = '%s/%s' % (dir_path, img_file)
@ -619,7 +635,10 @@ class NetAppNfsDriver(driver.ManageableVD,
def _post_clone_image(self, volume):
"""Do operations post image cloning."""
LOG.info('Performing post clone for %s', volume['name'])
vol_path = self.local_path(volume)
share = volume_utils.extract_host(volume['host'], level='pool')
vol_path = self._get_volume_path(share, volume['name'])
if self._discover_file_till_timeout(vol_path):
self._set_rw_permissions(vol_path)
self._resize_image_file(vol_path, volume['size'])

View File

@ -458,39 +458,6 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
LOG.exception('Exec of "rm" command on backing file for'
' %s was unsuccessful.', snapshot['id'])
@utils.trace_method
def copy_image_to_volume(self, context, volume, image_service, image_id):
"""Fetch the image from image_service and write it to the volume."""
copy_success = False
try:
major, minor = self.zapi_client.get_ontapi_version()
col_path = self.configuration.netapp_copyoffload_tool_path
# Search the local image cache before attempting copy offload
cache_result = self._find_image_in_cache(image_id)
if cache_result:
copy_success = self._copy_from_cache(volume, image_id,
cache_result)
if copy_success:
LOG.info('Copied image %(img)s to volume %(vol)s '
'using local image cache.',
{'img': image_id, 'vol': volume['id']})
# Image cache was not present, attempt copy offload workflow
if (not copy_success and col_path and
major == 1 and minor >= 20):
LOG.debug('No result found in image cache')
self._copy_from_img_service(context, volume, image_service,
image_id)
LOG.info('Copied image %(img)s to volume %(vol)s using'
' copy offload workflow.',
{'img': image_id, 'vol': volume['id']})
copy_success = True
except Exception:
LOG.exception('Copy offload workflow unsuccessful.')
finally:
if not copy_success:
super(NetAppCmodeNfsDriver, self).copy_image_to_volume(
context, volume, image_service, image_id)
def _get_ip_verify_on_cluster(self, host):
"""Verifies if host on same cluster and returns ip."""
ip = na_utils.resolve_hostname(host)
@ -502,13 +469,13 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
def _copy_from_cache(self, volume, image_id, cache_result):
"""Try copying image file_name from cached file_name."""
LOG.debug("Trying copy from cache using copy offload.")
copied = False
cache_copy, found_local = self._find_image_location(cache_result,
volume['id'])
volume)
try:
if found_local:
LOG.debug("Trying copy from cache using cloning.")
(nfs_share, file_name) = cache_copy
self._clone_file_dst_exists(
nfs_share, file_name, volume['name'], dest_exists=True)
@ -517,17 +484,14 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
copied = True
elif (cache_copy and
self.configuration.netapp_copyoffload_tool_path):
LOG.debug("Trying copy from cache using copy offload.")
self._copy_from_remote_cache(volume, image_id, cache_copy)
copied = True
if copied:
self._post_clone_image(volume)
except Exception:
LOG.exception('Error in workflow copy from cache.')
return copied
def _find_image_location(self, cache_result, volume_id):
def _find_image_location(self, cache_result, volume):
"""Finds the location of a cached image.
Returns image location local to the NFS share, that matches the
@ -537,7 +501,10 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
found_local_copy = False
cache_copy = None
provider_location = self._get_provider_location(volume_id)
provider_location = volume_utils.extract_host(volume['host'],
level='pool')
for res in cache_result:
(share, file_name) = res
if share == provider_location:
@ -575,10 +542,11 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
return src_ip, src_path
def _get_destination_ip_and_path(self, volume):
dest_ip = self._get_ip_verify_on_cluster(
self._get_host_ip(volume['id']))
dest_path = os.path.join(self._get_export_path(
volume['id']), volume['name'])
share = volume_utils.extract_host(volume['host'], level='pool')
share_ip_and_path = share.split(":")
dest_ip = self._get_ip_verify_on_cluster(share_ip_and_path[0])
dest_path = os.path.join(share_ip_and_path[1], volume['name'])
return dest_ip, dest_path
def _clone_file_dst_exists(self, share, src_name, dst_name,
@ -591,11 +559,14 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
def _copy_from_img_service(self, context, volume, image_service,
image_id):
"""Copies from the image service using copy offload."""
LOG.debug("Trying copy from image service using copy offload.")
image_loc = image_service.get_location(context, image_id)
locations = self._construct_image_nfs_url(image_loc)
src_ip = None
selected_loc = None
cloned = False
# this will match the first location that has a valid IP on cluster
for location in locations:
conn, dr = self._check_get_nfs_path_segs(location)
@ -610,32 +581,31 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
raise exception.NotFound(_("Source host details not found."))
(__, ___, img_file) = selected_loc.rpartition('/')
src_path = os.path.join(dr, img_file)
dst_ip = self._get_ip_verify_on_cluster(self._get_host_ip(
volume['id']))
dst_ip, vol_path = self._get_destination_ip_and_path(volume)
share_path = vol_path.rsplit("/", 1)[0]
dst_share = dst_ip + ':' + share_path
# tmp file is required to deal with img formats
tmp_img_file = six.text_type(uuid.uuid4())
col_path = self.configuration.netapp_copyoffload_tool_path
img_info = image_service.show(context, image_id)
dst_share = self._get_provider_location(volume['id'])
self._check_share_can_hold_size(dst_share, img_info['size'])
run_as_root = self._execute_as_root
dst_dir = self._get_mount_point_for_share(dst_share)
dst_img_local = os.path.join(dst_dir, tmp_img_file)
try:
# If src and dst share not equal
if (('%s:%s' % (src_ip, dr)) !=
('%s:%s' % (dst_ip, self._get_export_path(volume['id'])))):
dst_img_serv_path = os.path.join(
self._get_export_path(volume['id']), tmp_img_file)
# Always run copy offload as regular user, it's sufficient
# and rootwrap doesn't allow copy offload to run as root
# anyways.
self._execute(col_path, src_ip, dst_ip, src_path,
dst_img_serv_path, run_as_root=False,
check_exit_code=0)
else:
self._clone_file_dst_exists(dst_share, img_file, tmp_img_file)
dst_img_serv_path = os.path.join(
share_path, tmp_img_file)
# Always run copy offload as regular user, it's sufficient
# and rootwrap doesn't allow copy offload to run as root
# anyways.
self._execute(col_path, src_ip, dst_ip, src_path,
dst_img_serv_path, run_as_root=False,
check_exit_code=0)
self._discover_file_till_timeout(dst_img_local, timeout=120)
LOG.debug('Copied image %(img)s to tmp file %(tmp)s.',
{'img': image_id, 'tmp': tmp_img_file})
@ -677,11 +647,13 @@ class NetAppCmodeNfsDriver(nfs_base.NetAppNfsDriver,
finally:
if os.path.exists(dst_img_conv_local):
self._delete_file_at_path(dst_img_conv_local)
self._post_clone_image(volume)
cloned = True
finally:
if os.path.exists(dst_img_local):
self._delete_file_at_path(dst_img_local)
return cloned
@utils.trace_method
def unmanage(self, volume):
"""Removes the specified volume from Cinder management.

View File

@ -0,0 +1,5 @@
---
fixes:
- |
Fixed bug 1632333 with the NetApp ONTAP Driver. Now the copy offload method is invoked
early to avoid downloading Glance images twice.