Merge "NetApp ONTAP: Copy offload bugfix"
This commit is contained in:
commit
4e51d33861
|
@ -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 = []
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue