Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 140 additions & 11 deletions cinder/tests/unit/volume/drivers/netapp/dataontap/test_nfs_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from cinder import context
from cinder import exception
from cinder.image import image_utils
from cinder.objects import fields
from cinder.tests.unit import fake_snapshot
from cinder.tests.unit import fake_volume
Expand Down Expand Up @@ -550,41 +551,169 @@ def test__update_volume_stats(self):
self.assertRaises(NotImplementedError,
self.driver._update_volume_stats)

@mock.patch.object(image_utils, 'resize_image')
@mock.patch.object(image_utils, 'qemu_img_info')
@mock.patch.object(image_utils, 'fetch_to_raw')
def test_copy_image_to_volume_cache_uses_image_size(
self,
mock_fetch_to_raw,
mock_qemu_img_info,
mock_resize_image):

volume = fake_volume.fake_volume_obj(
self.ctxt,
id=fake.VOLUME_ID,
size=10,
host='fake-host')

image_id = 'image-1'
image_size = 10

mock_qemu_img_info.return_value.virtual_size = image_size * units.Gi

self.mock_object(
self.driver,
'_is_flexgroup',
return_value=False)

mock_register = self.mock_object(
self.driver,
'_register_image_in_cache')

self.mock_object(
self.driver,
'local_path',
return_value=f'/tmp/{volume.id}')

self.driver.copy_image_to_volume(
mock.sentinel.context,
volume,
mock.sentinel.image_service,
image_id)

mock_fetch_to_raw.assert_called_once_with(
mock.sentinel.context,
mock.sentinel.image_service,
image_id,
f'/tmp/{volume.id}',
self.driver.configuration.volume_dd_blocksize,
size=volume.size,
run_as_root=self.driver._execute_as_root,
disable_sparse=False)

mock_qemu_img_info.assert_called_once_with(
f'/tmp/{volume.id}',
run_as_root=self.driver._execute_as_root)

mock_resize_image.assert_called_once_with(
f'/tmp/{volume.id}',
image_size,
run_as_root=self.driver._execute_as_root)

mock_register.assert_called_once_with(
volume, image_id)

@mock.patch.object(image_utils, 'resize_image')
@mock.patch.object(image_utils, 'qemu_img_info')
@mock.patch.object(image_utils, 'fetch_to_raw')
def test_copy_image_to_volume_image_size_mismatch(
self,
mock_fetch_to_raw,
mock_qemu_img_info,
mock_resize_image):

volume = fake_volume.fake_volume_obj(
self.ctxt,
id=fake.VOLUME_ID,
size=30,
host='fake-host')

image_id = 'image-1'

self.mock_object(
self.driver,
'_is_flexgroup',
return_value=False)

mock_register = self.mock_object(
self.driver,
'_register_image_in_cache')

self.mock_object(
self.driver,
'local_path',
return_value=f'/tmp/{volume.id}')

mock_qemu_img_info.return_value.virtual_size = 10 * units.Gi

self.assertRaises(
exception.ImageUnacceptable,
self.driver.copy_image_to_volume,
mock.sentinel.context,
volume,
mock.sentinel.image_service,
image_id)

mock_fetch_to_raw.assert_called_once()
mock_qemu_img_info.assert_called_once_with(
f'/tmp/{volume.id}',
run_as_root=self.driver._execute_as_root)
mock_resize_image.assert_called_once_with(
f'/tmp/{volume.id}',
30,
run_as_root=self.driver._execute_as_root)

mock_register.assert_called_once()

def test_copy_image_to_volume_base_exception(self):
mock_info_log = self.mock_object(nfs_base.LOG, 'info')
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
self.mock_object(remotefs.RemoteFSDriver, 'copy_image_to_volume',
side_effect=exception.NfsException)
self.mock_object(self.driver, 'local_path',
mock.Mock(side_effect=exception.NfsException))

self.assertRaises(exception.NfsException,
self.driver.copy_image_to_volume,
'fake_context', fake.NFS_VOLUME,
'fake_img_service', fake.IMAGE_FILE_ID)
mock_info_log.assert_not_called()

def test_copy_image_to_volume(self):
@mock.patch.object(image_utils, 'resize_image')
@mock.patch.object(image_utils, 'qemu_img_info')
@mock.patch.object(image_utils, 'fetch_to_raw')
def test_copy_image_to_volume(self,
mock_fetch_to_raw,
mock_qemu_img_info,
mock_resize_image):
volume = fake_volume.fake_volume_obj(self.ctxt, **fake.NFS_VOLUME)
mock_qemu_img_info.return_value.virtual_size = (
volume.size * units.Gi)

mock_log = self.mock_object(nfs_base, 'LOG')
self.mock_object(self.driver, '_is_flexgroup',
return_value=False)
self.mock_object(self.driver, '_is_flexgroup_clone_file_supported',
return_value=True)
self.mock_object(self.driver, '_ensure_flexgroup_not_in_cg')
mock_copy_image = self.mock_object(
remotefs.RemoteFSDriver, 'copy_image_to_volume')
self.mock_object(self.driver, 'local_path')
mock_register_image = self.mock_object(
self.driver, '_register_image_in_cache')

image_service = mock.Mock()
image_service.show.return_value = {
'disk_format': 'raw',
'container_format': 'bare',
}
self.driver.copy_image_to_volume('fake_context',
fake.NFS_VOLUME,
'fake_img_service',
volume,
image_service,
fake.IMAGE_FILE_ID)

mock_copy_image.assert_called_once_with(
'fake_context', fake.NFS_VOLUME, 'fake_img_service',
fake.IMAGE_FILE_ID, disable_sparse=False)
self.assertEqual(1, mock_fetch_to_raw.call_count)
self.assertEqual(1, mock_qemu_img_info.call_count)
self.assertEqual(1, mock_resize_image.call_count)
self.assertEqual(1, mock_log.info.call_count)
mock_register_image.assert_called_once_with(
fake.NFS_VOLUME, fake.IMAGE_FILE_ID)
volume, fake.IMAGE_FILE_ID)

@ddt.data(None, Exception)
def test__register_image_in_cache(self, exc):
Expand Down
24 changes: 20 additions & 4 deletions cinder/volume/drivers/netapp/dataontap/nfs_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,19 +511,35 @@ def copy_image_to_volume(self, context, volume, image_service, image_id,
disable_sparse=False):
"""Fetch the image from image_service and write it to the volume."""
self._ensure_flexgroup_not_in_cg(volume)
super(NetAppNfsDriver, self).copy_image_to_volume(
context, volume, image_service, image_id,
disable_sparse=disable_sparse)
# modified behaviour for cinder/volume/drivers/remotefs.py +530
image_utils.fetch_to_raw(context,
image_service,
image_id,
self.local_path(volume),
self.configuration.volume_dd_blocksize,
size=volume.size,
run_as_root=self._execute_as_root,
disable_sparse=disable_sparse)
LOG.info('Copied image to volume %s using regular download.',
volume['id'])

if (not self._is_flexgroup(host=volume['host']) or
self._is_flexgroup_clone_file_supported()):
# NOTE(felipe_rodrigues): NetApp image cache relies on the
# FlexClone file, which is only available for the earliest
# versions of FlexGroup.
self._register_image_in_cache(volume, image_id)

image_utils.resize_image(self.local_path(volume), volume.size,
run_as_root=self._execute_as_root)
data = image_utils.qemu_img_info(self.local_path(volume),
run_as_root=self._execute_as_root)
virt_size = int(data.virtual_size // units.Gi)
if virt_size != volume.size:
raise exception.ImageUnacceptable(
image_id=image_id,
reason=(_("Expected volume size was %d") % volume.size)
+ (_(" but size is now %d") % virt_size))

def _register_image_in_cache(self, volume, image_id):
"""Stores image in the cache."""
file_name = 'img-cache-%s' % image_id
Expand Down