"""Unit tests for the NetApp-specific NFS driver module."""
from lxml import etree
+import mock
import mox
from mox import IgnoreArg
from mox import IsA
from cinder.volume import configuration as conf
from cinder.volume.drivers.netapp import api
from cinder.volume.drivers.netapp import nfs as netapp_nfs
+from cinder.volume.drivers.netapp import utils
from oslo.config import cfg
def test_check_share_in_use_incorrect_host(self):
drv = self._driver
mox = self.mox
- mox.StubOutWithMock(drv, '_resolve_hostname')
- drv._resolve_hostname(IgnoreArg()).AndRaise(Exception())
+ mox.StubOutWithMock(utils, 'resolve_hostname')
+ utils.resolve_hostname(IgnoreArg()).AndRaise(Exception())
mox.ReplayAll()
share = drv._check_share_in_use('incorrect:8989', '/dir')
mox.VerifyAll()
drv = self._driver
mox = self.mox
drv._mounted_shares = ['127.0.0.1:/dir/share']
- mox.StubOutWithMock(drv, '_resolve_hostname')
+ mox.StubOutWithMock(utils, 'resolve_hostname')
mox.StubOutWithMock(drv, '_share_match_for_ip')
- drv._resolve_hostname(IgnoreArg()).AndReturn('10.22.33.44')
+ utils.resolve_hostname(IgnoreArg()).AndReturn('10.22.33.44')
drv._share_match_for_ip(
'10.22.33.44', ['127.0.0.1:/dir/share']).AndReturn('share')
mox.ReplayAll()
self.fail("Unexpected direct url.")
+class NetappDirectCmodeNfsDriverOnlyTestCase(test.TestCase):
+ """Test direct NetApp C Mode driver only and not inherit."""
+
+ def setUp(self):
+ super(NetappDirectCmodeNfsDriverOnlyTestCase, self).setUp()
+ self._custom_setup()
+
+ def _custom_setup(self):
+ kwargs = {}
+ kwargs['netapp_mode'] = 'proxy'
+ kwargs['configuration'] = create_configuration()
+ self._driver = netapp_nfs.NetAppDirectCmodeNfsDriver(**kwargs)
+ self._driver.ssc_enabled = True
+ self._driver.configuration.netapp_copyoffload_tool_path = 'cof_path'
+
+ def test_copy_img_to_vol_copyoffload_success(self):
+ drv = self._driver
+ context = object()
+ volume = {'id': 'vol_id', 'name': 'name'}
+ image_service = object()
+ image_id = 'image_id'
+ drv._client = mock.Mock()
+ drv._client.get_api_version = mock.Mock(return_value=(1, 20))
+ drv._try_copyoffload = mock.Mock()
+ drv._get_provider_location = mock.Mock(return_value='share')
+ drv._get_vol_for_share = mock.Mock(return_value='vol')
+ drv._update_stale_vols = mock.Mock()
+
+ drv.copy_image_to_volume(context, volume, image_service, image_id)
+ drv._try_copyoffload.assert_called_once_with(context, volume,
+ image_service,
+ image_id)
+ drv._update_stale_vols.assert_called_once_with('vol')
+
+ def test_copy_img_to_vol_copyoffload_failure(self):
+ drv = self._driver
+ context = object()
+ volume = {'id': 'vol_id', 'name': 'name'}
+ image_service = object()
+ image_id = 'image_id'
+ drv._client = mock.Mock()
+ drv._client.get_api_version = mock.Mock(return_value=(1, 20))
+ drv._try_copyoffload = mock.Mock(side_effect=Exception())
+ netapp_nfs.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._update_stale_vols = mock.Mock()
+
+ drv.copy_image_to_volume(context, volume, image_service, image_id)
+ drv._try_copyoffload.assert_called_once_with(context, volume,
+ image_service,
+ image_id)
+ netapp_nfs.NetAppNFSDriver.copy_image_to_volume.\
+ assert_called_once_with(context, volume, image_service, image_id)
+ drv._update_stale_vols.assert_called_once_with('vol')
+
+ def test_copyoffload_frm_cache_success(self):
+ drv = self._driver
+ context = object()
+ volume = {'id': 'vol_id', 'name': 'name'}
+ image_service = object()
+ image_id = 'image_id'
+ drv._find_image_in_cache = mock.Mock(return_value=[('share', 'img')])
+ drv._copy_from_cache = mock.Mock(return_value=True)
+
+ drv._try_copyoffload(context, volume, image_service, image_id)
+ drv._copy_from_cache.assert_called_once_with(volume,
+ image_id,
+ [('share', 'img')])
+
+ def test_copyoffload_frm_img_service_success(self):
+ drv = self._driver
+ context = object()
+ volume = {'id': 'vol_id', 'name': 'name'}
+ image_service = object()
+ 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=[])
+ drv._copy_from_img_service = mock.Mock()
+
+ drv._try_copyoffload(context, volume, image_service, image_id)
+ drv._copy_from_img_service.assert_called_once_with(context,
+ volume,
+ image_service,
+ image_id)
+
+ def test_cache_copyoffload_workflow_success(self):
+ drv = self._driver
+ volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
+ 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('cof_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')
+
+ @mock.patch.object(image_utils, 'qemu_img_info')
+ def test_img_service_raw_copyoffload_workflow_success(self,
+ mock_qemu_img_info):
+ drv = self._driver
+ volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
+ image_id = 'image_id'
+ context = object()
+ image_service = mock.Mock()
+ image_service.get_location.return_value = ('nfs://ip1/openstack/img',
+ None)
+ image_service.show.return_value = {'size': 1,
+ 'disk_format': 'raw'}
+
+ drv._check_get_nfs_path_segs = mock.Mock(return_value=
+ ('ip1', '/openstack'))
+ 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._get_provider_location = mock.Mock(return_value='share')
+ drv._execute = mock.Mock()
+ drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point')
+ drv._discover_file_till_timeout = mock.Mock(return_value=True)
+ img_inf = mock.Mock()
+ img_inf.file_format = 'raw'
+ mock_qemu_img_info.return_value = img_inf
+ drv._check_share_can_hold_size = mock.Mock()
+ drv._move_nfs_file = mock.Mock(return_value=True)
+ drv._delete_file = mock.Mock()
+ drv._clone_file_dst_exists = mock.Mock()
+ drv._post_clone_image = mock.Mock()
+
+ drv._copy_from_img_service(context, volume, image_service, image_id)
+ 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)
+
+ assert drv._execute.call_count == 1
+ drv._post_clone_image.assert_called_with(volume)
+
+ @mock.patch.object(image_utils, 'convert_image')
+ @mock.patch.object(image_utils, 'qemu_img_info')
+ @mock.patch('os.path.exists')
+ def test_img_service_qcow2_copyoffload_workflow_success(self, mock_exists,
+ mock_qemu_img_info,
+ mock_cvrt_image):
+ drv = self._driver
+ volume = {'id': 'vol_id', 'name': 'name', 'size': 1}
+ image_id = 'image_id'
+ context = object()
+ image_service = mock.Mock()
+ image_service.get_location.return_value = ('nfs://ip1/openstack/img',
+ None)
+ image_service.show.return_value = {'size': 1,
+ 'disk_format': 'qcow2'}
+ drv._check_get_nfs_path_segs = mock.Mock(return_value=
+ ('ip1', '/openstack'))
+
+ 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._get_provider_location = mock.Mock(return_value='share')
+ drv._execute = mock.Mock()
+ drv._get_mount_point_for_share = mock.Mock(return_value='mnt_point')
+ img_inf = mock.Mock()
+ img_inf.file_format = 'raw'
+ mock_qemu_img_info.return_value = img_inf
+ drv._check_share_can_hold_size = mock.Mock()
+
+ drv._move_nfs_file = mock.Mock(return_value=True)
+ drv._delete_file = mock.Mock()
+ drv._clone_file_dst_exists = mock.Mock()
+ drv._post_clone_image = mock.Mock()
+
+ drv._copy_from_img_service(context, volume, image_service, image_id)
+ 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)
+ assert mock_cvrt_image.call_count == 1
+ assert drv._execute.call_count == 1
+ assert drv._delete_file.call_count == 2
+ drv._clone_file_dst_exists.call_count == 1
+ drv._post_clone_image.assert_called_with(volume)
+
+
class NetappDirect7modeNfsDriverTestCase(NetappDirectCmodeNfsDriverTestCase):
"""Test direct NetApp C Mode driver."""
def _custom_setup(self):
import copy
import os
import re
-import socket
from threading import Timer
import time
import urlparse
+import uuid
from cinder import exception
from cinder.image import image_utils
from cinder.volume.drivers.netapp.options import netapp_cluster_opts
from cinder.volume.drivers.netapp.options import netapp_connection_opts
from cinder.volume.drivers.netapp.options import netapp_img_cache_opts
+from cinder.volume.drivers.netapp.options import netapp_nfs_extra_opts
from cinder.volume.drivers.netapp.options import netapp_transport_opts
from cinder.volume.drivers.netapp import ssc_utils
from cinder.volume.drivers.netapp import utils as na_utils
"""Fetch the image from image_service and write it to the volume."""
super(NetAppNFSDriver, self).copy_image_to_volume(
context, volume, image_service, image_id)
- LOG.info(_('Copied image to volume %s'), volume['name'])
+ LOG.info(_('Copied image to volume %s using regular download.'),
+ volume['name'])
self._register_image_in_cache(volume, image_id)
def _register_image_in_cache(self, volume, image_id):
dir = self._get_mount_point_for_share(share)
file_path = '%s/%s' % (dir, dst)
if not os.path.exists(file_path):
- LOG.info(_('Cloning img from cache for %s'), dst)
+ LOG.info(_('Cloning from cache to destination %s'), dst)
self._clone_volume(src, dst, volume_id=None, share=share)
_do_clone()
post_clone = self._post_clone_image(volume)
except Exception as e:
msg = e.msg if getattr(e, 'msg', None) else e.__str__()
- LOG.warn(_('Unexpected exception in cloning image'
+ LOG.info(_('Image cloning unsuccessful for image'
' %(image_id)s. Message: %(msg)s')
% {'image_id': image_id, 'msg': msg})
vol_path = self.local_path(volume)
def _direct_nfs_clone(self, volume, image_location, image_id):
"""Clone directly in nfs share."""
- LOG.info(_('Cloning image %s directly in share'), image_id)
+ LOG.info(_('Checking image clone %s from glance share.'), image_id)
cloned = False
image_location = self._construct_image_nfs_url(image_location)
share = self._is_cloneable_share(image_location)
retry_seconds = retry_seconds - sleep_interval
def _is_cloneable_share(self, image_location):
- """Finds if the image at location is cloneable.
+ """Finds if the image at location is cloneable."""
+ conn, dr = self._check_get_nfs_path_segs(image_location)
+ return self._check_share_in_use(conn, dr)
- WebNFS url format with relative-path is supported.
- Accepting all characters in path-names and checking
- against the mounted shares which will contain only
- allowed path segments.
- """
+ def _check_get_nfs_path_segs(self, image_location):
+ """Checks if the nfs path format is matched.
- nfs_loc_pattern =\
- '^nfs://(([\w\-\.]+:{1}[\d]+|[\w\-\.]+)(/[^\/].*)*(/[^\/\\\\]+)$)'
- matched = re.match(nfs_loc_pattern, image_location, flags=0)
- if not matched:
- LOG.debug(_('Image location not in the'
- ' expected format %s'), image_location)
- return None
- conn = matched.group(2)
- dir = matched.group(3) or '/'
- return self._check_share_in_use(conn, dir)
+ WebNFS url format with relative-path is supported.
+ Accepting all characters in path-names and checking
+ against the mounted shares which will contain only
+ allowed path segments. Returns connection and dir details.
+ """
+ conn, dr = None, None
+ if image_location:
+ nfs_loc_pattern =\
+ ('^nfs://(([\w\-\.]+:{1}[\d]+|[\w\-\.]+)(/[^\/].*)'
+ '*(/[^\/\\\\]+)$)')
+ matched = re.match(nfs_loc_pattern, image_location, flags=0)
+ if not matched:
+ LOG.debug(_('Image location not in the'
+ ' expected format %s'), image_location)
+ else:
+ conn = matched.group(2)
+ dr = matched.group(3) or '/'
+ return (conn, dr)
def _share_match_for_ip(self, ip, shares):
"""Returns the share that is served by ip.
try:
if conn:
host = conn.split(':')[0]
- ip = self._resolve_hostname(host)
+ ip = na_utils.resolve_hostname(host)
share_candidates = []
for sh in self._mounted_shares:
sh_exp = sh.split(':')[1]
"""
direct_url, locations = image_location
+ if not direct_url and not locations:
+ raise exception.NotFound(_('Image location not present.'))
# Locations will be always a list of one until
# bp multiple-image-locations is introduced
"""Checks if share is compatible with volume to host it."""
raise NotImplementedError()
- def _resolve_hostname(self, hostname):
- """Resolves hostname to IP address."""
- res = socket.getaddrinfo(hostname, None)[0]
- family, socktype, proto, canonname, sockaddr = res
- return sockaddr[0]
+ def _check_share_can_hold_size(self, share, size):
+ """Checks if volume can hold image with size."""
+ tot_size, tot_available, tot_allocated = self._get_capacity_info(share)
+ if tot_available < size:
+ msg = _("Container size smaller than required file size.")
+ raise exception.VolumeDriverException(msg)
+
+ def _move_nfs_file(self, source_path, dest_path):
+ """Moves source to destination."""
+ @utils.synchronized(dest_path, external=True)
+ def _move_file(src, dst):
+ if os.path.exists(dst):
+ LOG.warn(_("Destination %s already exists."), dst)
+ return False
+ self._execute('mv', src, dst, run_as_root=True)
+ return True
+
+ try:
+ return _move_file(source_path, dest_path)
+ except Exception as e:
+ LOG.warn(_('Exception moving file %(src)s. Message - %(e)s')
+ % {'src': source_path, 'e': e})
+ return False
class NetAppDirectNfsDriver (NetAppNFSDriver):
def __init__(self, *args, **kwargs):
super(NetAppDirectCmodeNfsDriver, self).__init__(*args, **kwargs)
self.configuration.append_config_values(netapp_cluster_opts)
+ self.configuration.append_config_values(netapp_nfs_extra_opts)
def _do_custom_setup(self, client):
"""Do the customized set up on client for cluster mode."""
net_if_iter.add_new_child('max-records', '10')
query = NaElement('query')
net_if_iter.add_child_elem(query)
- query.add_node_with_children('net-interface-info',
- **{'address': self._resolve_hostname(ip)})
+ query.add_node_with_children(
+ 'net-interface-info', **{'address': na_utils.resolve_hostname(ip)})
result = self._invoke_successfully(net_if_iter)
if result.get_child_content('num-records') and\
int(result.get_child_content('num-records')) >= 1:
%(vserver)s and junction path %(junction)s
""") % msg_fmt)
- def _clone_file(self, volume, src_path, dest_path, vserver=None):
+ def _clone_file(self, volume, src_path, dest_path, vserver=None,
+ dest_exists=False):
"""Clones file on vserver."""
msg = _("""Cloning with params volume %(volume)s, src %(src_path)s,
dest %(dest_path)s, vserver %(vserver)s""")
'clone-create',
**{'volume': volume, 'source-path': src_path,
'destination-path': dest_path})
+ major, minor = self._client.get_api_version()
+ if major == 1 and minor >= 20 and dest_exists:
+ clone_create.add_new_child('destination-exists', 'true')
self._invoke_successfully(clone_create, vserver)
def _update_volume_stats(self):
for sh in self._mounted_shares:
host = sh.split(':')[0]
junction = sh.split(':')[1]
- ip = self._resolve_hostname(host)
+ ip = na_utils.resolve_hostname(host)
if (self._ip_in_ifs(ip, vs_ifs) and
junction == vol.id['junction_path']):
mnt_share_vols.add(vol)
if netapp_vol:
self._update_stale_vols(volume=netapp_vol)
+ 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._client.get_api_version()
+ col_path = self.configuration.netapp_copyoffload_tool_path
+ if (major == 1 and minor >= 20 and col_path):
+ self._try_copyoffload(context, volume, image_service, image_id)
+ copy_success = True
+ LOG.info(_('Copied image %(img)s to volume %(vol)s using copy'
+ ' offload workflow.')
+ % {'img': image_id, 'vol': volume['id']})
+ else:
+ LOG.debug(_("Copy offload either not configured or"
+ " unsupported."))
+ except Exception as e:
+ LOG.exception(_('Copy offload workflow unsuccessful. %s'), e)
+ finally:
+ if not copy_success:
+ super(NetAppDirectCmodeNfsDriver, self).copy_image_to_volume(
+ context, volume, image_service, image_id)
+ if self.ssc_enabled:
+ sh = self._get_provider_location(volume['id'])
+ self._update_stale_vols(self._get_vol_for_share(sh))
+
+ def _try_copyoffload(self, context, volume, image_service, image_id):
+ """Tries server side file copy offload."""
+ copied = False
+ cache_result = self._find_image_in_cache(image_id)
+ if cache_result:
+ copied = self._copy_from_cache(volume, image_id, cache_result)
+ if not cache_result or not copied:
+ self._copy_from_img_service(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)
+ vserver = self._get_vserver_for_ip(ip)
+ if not vserver:
+ raise exception.NotFound(_("No vserver owning the ip %s.") % ip)
+ return ip
+
+ 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
+ for res in cache_result:
+ try:
+ (share, file_name) = res
+ LOG.debug(_("Found cache file_name on share %s."), share)
+ if share != self._get_provider_location(volume['id']):
+ col_path = self.configuration.netapp_copyoffload_tool_path
+ src_ip = self._get_ip_verify_on_cluster(
+ share.split(':')[0])
+ src_path = os.path.join(share.split(':')[1], file_name)
+ dst_ip = self._get_ip_verify_on_cluster(self._get_host_ip(
+ volume['id']))
+ dst_path = os.path.join(
+ self._get_export_path(volume['id']), volume['name'])
+ self._execute(col_path, src_ip, dst_ip,
+ src_path, dst_path, run_as_root=False,
+ check_exit_code=0)
+ self._register_image_in_cache(volume, image_id)
+ LOG.debug(_("Copied image from cache to volume %s using"
+ " copy offload."), volume['id'])
+ else:
+ self._clone_file_dst_exists(share, file_name,
+ volume['name'],
+ dest_exists=True)
+ LOG.debug(_("Copied image from cache to volume %s using"
+ " cloning."), volume['id'])
+ self._post_clone_image(volume)
+ copied = True
+ break
+ except Exception as e:
+ LOG.exception(_('Error in workflow copy from cache. %s.'), e)
+ return copied
+
+ def _clone_file_dst_exists(self, share, src_name, dst_name,
+ dest_exists=False):
+ """Clone file even if dest exists."""
+ (vserver, exp_volume) = self._get_vserver_and_exp_vol(share=share)
+ self._clone_file(exp_volume, src_name, dst_name, vserver,
+ dest_exists=dest_exists)
+
+ 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)
+ image_loc = self._construct_image_nfs_url(image_loc)
+ conn, dr = self._check_get_nfs_path_segs(image_loc)
+ if conn:
+ src_ip = self._get_ip_verify_on_cluster(conn.split(':')[0])
+ else:
+ raise exception.NotFound(_("Source host details not found."))
+ (__, ___, img_file) = image_loc.rpartition('/')
+ src_path = os.path.join(dr, img_file)
+ dst_ip = self._get_ip_verify_on_cluster(self._get_host_ip(
+ volume['id']))
+ # tmp file is required to deal with img formats
+ tmp_img_file = str(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'])
+
+ 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)
+ 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_dir = self._get_mount_point_for_share(dst_share)
+ dst_img_local = os.path.join(dst_dir, tmp_img_file)
+ 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})
+ dst_img_cache_local = os.path.join(dst_dir,
+ 'img-cache-%s' % (image_id))
+ if img_info['disk_format'] == 'raw':
+ LOG.debug(_('Image is raw %s.'), image_id)
+ self._clone_file_dst_exists(dst_share, tmp_img_file,
+ volume['name'], dest_exists=True)
+ self._move_nfs_file(dst_img_local, dst_img_cache_local)
+ LOG.debug(_('Copied raw image %(img)s to volume %(vol)s.')
+ % {'img': image_id, 'vol': volume['id']})
+ else:
+ LOG.debug(_('Image will be converted to raw %s.'), image_id)
+ img_conv = str(uuid.uuid4())
+ dst_img_conv_local = os.path.join(dst_dir, img_conv)
+
+ # Checking against image size which is approximate check
+ self._check_share_can_hold_size(dst_share, img_info['size'])
+ try:
+ image_utils.convert_image(dst_img_local,
+ dst_img_conv_local, 'raw')
+ data = image_utils.qemu_img_info(dst_img_conv_local)
+ if data.file_format != "raw":
+ raise exception.InvalidResults(
+ _("Converted to raw, but format is now %s.")
+ % data.file_format)
+ else:
+ self._clone_file_dst_exists(dst_share, img_conv,
+ volume['name'],
+ dest_exists=True)
+ self._move_nfs_file(dst_img_conv_local,
+ dst_img_cache_local)
+ LOG.debug(_('Copied locally converted raw image'
+ ' %(img)s to volume %(vol)s.')
+ % {'img': image_id, 'vol': volume['id']})
+ finally:
+ if os.path.exists(dst_img_conv_local):
+ self._delete_file(dst_img_conv_local)
+ self._post_clone_image(volume)
+ finally:
+ if os.path.exists(dst_img_local):
+ self._delete_file(dst_img_local)
+
class NetAppDirect7modeNfsDriver (NetAppDirectNfsDriver):
"""Executes commands related to volumes on 7 mode."""