VMware: Set target ESX host for backing VM clone

The backing VM corresponding to a volume is cloned during create volume
from another volume, snapshot or image. The backing VM is also cloned
during retype and backup restore. Currently, the target ESX host is
unset while calling vCenter CloneVM_Task API and hence the source ESX
host is used as the target. If the destination datastore returned by
the datastore selection logic is not accessible to the source host,
clone fails. This patch fixes the problem by setting the target ESX
host (returned by datastore selection logic) while invoking clone.

Conflicts:
        cinder/volume/drivers/vmware/vmdk.py

Closes-Bug: #1380602
Change-Id: I030d5ce6378fb70f7f98356114825abc12297687
(cherry picked from commit 9e590a6c85)
This commit is contained in:
Vipin Balachandran 2014-11-19 16:08:45 +05:30
parent 5621f16fbf
commit 0d60ddc767
4 changed files with 34 additions and 22 deletions

View File

@ -1051,7 +1051,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
select_ds_for_volume.assert_called_once_with(volume)
vops.clone_backing.assert_called_once_with(
volume['name'], backing, None, volumeops.FULL_CLONE_TYPE,
summary.datastore, disk_type)
summary.datastore, disk_type, mock.sentinel.host)
vops.delete_backing.assert_called_once_with(backing)
self.assertFalse(extend_disk.called)
@ -1554,7 +1554,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
vops.rename_backing.assert_called_once_with(backing, uuid)
vops.clone_backing.assert_called_once_with(
vol['name'], backing, None, volumeops.FULL_CLONE_TYPE,
datastore, vmdk.THIN_VMDK_TYPE)
datastore, vmdk.THIN_VMDK_TYPE, host)
delete_temp_backing.assert_called_once_with(backing)
vops.change_backing_profile.assert_called_once_with(clone,
profile_id)
@ -1766,7 +1766,8 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
summary = mock.Mock()
summary.datastore = mock.sentinel.datastore
select_ds.return_value = (mock.ANY, mock.ANY, mock.ANY, summary)
select_ds.return_value = (mock.sentinel.host, mock.ANY, mock.ANY,
summary)
disk_type = vmdk.THIN_VMDK_TYPE
get_disk_type.return_value = disk_type
@ -1783,7 +1784,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
context, src_uuid, volume, tmp_file_path, backup_size)
vops.clone_backing.assert_called_once_with(
volume['name'], src, None, volumeops.FULL_CLONE_TYPE,
summary.datastore, disk_type)
summary.datastore, disk_type, mock.sentinel.host)
delete_temp_backing.assert_called_once_with(src)
create_backing.reset_mock()
@ -1805,7 +1806,7 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase):
context, src_uuid, volume, tmp_file_path, backup_size)
vops.clone_backing.assert_called_once_with(
dest_uuid, src, None, volumeops.FULL_CLONE_TYPE,
summary.datastore, disk_type)
summary.datastore, disk_type, mock.sentinel.host)
exp_rename_calls = [mock.call(backing, tmp_uuid),
mock.call(dest, volume['name'])]
self.assertEqual(exp_rename_calls, vops.rename_backing.call_args_list)
@ -2132,7 +2133,8 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
fake_backing,
fake_snapshot,
fake_type,
None)
None,
host=None)
# If the volume size is greater than the original snapshot size,
# _extend_vmdk_virtual_disk will be called.
_extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'],
@ -2177,7 +2179,8 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase):
fake_backing,
fake_snapshot,
volumeops.FULL_CLONE_TYPE,
fake_datastore)
fake_datastore,
host=fake_host)
# If the volume size is greater than the original snapshot size,
# _extend_vmdk_virtual_disk will be called.
_extend_vmdk_virtual_disk.assert_called_with(fake_volume['name'],

View File

@ -983,7 +983,7 @@ class VolumeOpsTestCase(test.TestCase):
self.assertEqual(mock.sentinel.new_backing, ret)
disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
backing, None)
backing, None, None)
expected = [mock.call(vim_util, 'get_object_property',
self.session.vim, backing, 'parent'),
mock.call(self.session.vim, 'CloneVM_Task', backing,
@ -999,24 +999,25 @@ class VolumeOpsTestCase(test.TestCase):
self.assertEqual(mock.sentinel.new_backing, ret)
disk_move_type = 'createNewChildDiskBacking'
get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
backing, None)
backing, None, None)
expected = [mock.call(vim_util, 'get_object_property',
self.session.vim, backing, 'parent'),
mock.call(self.session.vim, 'CloneVM_Task', backing,
folder=folder, name=name, spec=clone_spec)]
self.assertEqual(expected, self.session.invoke_api.mock_calls)
# Test disk type conversion.
# Test disk type conversion and target host.
clone_type = None
disk_type = 'thin'
host = mock.sentinel.host
self.session.invoke_api.reset_mock()
ret = self.vops.clone_backing(name, backing, snapshot, clone_type,
datastore, disk_type)
datastore, disk_type, host)
self.assertEqual(mock.sentinel.new_backing, ret)
disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
get_clone_spec.assert_called_with(datastore, disk_move_type, snapshot,
backing, disk_type)
backing, disk_type, host)
expected = [mock.call(vim_util, 'get_object_property',
self.session.vim, backing, 'parent'),
mock.call(self.session.vim, 'CloneVM_Task', backing,

View File

@ -1136,7 +1136,8 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
None,
volumeops.FULL_CLONE_TYPE,
datastore,
disk_type)
disk_type,
host)
self._delete_temp_backing(backing)
except Exception:
# Delete backing and virtual disk created from image.
@ -1499,7 +1500,8 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
new_backing = self.volumeops.clone_backing(
volume['name'], backing, None,
volumeops.FULL_CLONE_TYPE, datastore, new_disk_type)
volumeops.FULL_CLONE_TYPE, datastore, new_disk_type,
host)
self._delete_temp_backing(backing)
backing = new_backing
except error_util.VimException:
@ -1713,7 +1715,7 @@ class VMwareEsxVmdkDriver(driver.VolumeDriver):
disk_type = VMwareEsxVmdkDriver._get_disk_type(volume)
dest = self.volumeops.clone_backing(dest_name, src, None,
volumeops.FULL_CLONE_TYPE,
datastore, disk_type)
datastore, disk_type, host)
if new_backing:
LOG.debug("Created new backing: %s for restoring backup.",
dest_name)
@ -1978,12 +1980,14 @@ class VMwareVcVmdkDriver(VMwareEsxVmdkDriver):
:param src_vsize: the size of the source volume
"""
datastore = None
host = None
if not clone_type == volumeops.LINKED_CLONE_TYPE:
# Pick a datastore where to create the full clone under any host
(host, rp, folder, summary) = self._select_ds_for_volume(volume)
datastore = summary.datastore
clone = self.volumeops.clone_backing(volume['name'], backing,
snapshot, clone_type, datastore)
snapshot, clone_type, datastore,
host=host)
# If the volume size specified by the user is greater than
# the size of the source volume, the newly created volume will
# allocate the capacity to the size of the source volume in the backend

View File

@ -996,7 +996,7 @@ class VMwareVolumeOps(object):
return self._get_parent(backing, 'Folder')
def _get_clone_spec(self, datastore, disk_move_type, snapshot, backing,
disk_type):
disk_type, host=None):
"""Get the clone spec.
:param datastore: Reference to datastore
@ -1004,6 +1004,7 @@ class VMwareVolumeOps(object):
:param snapshot: Reference to snapshot
:param backing: Source backing VM
:param disk_type: Disk type of clone
:param host: Target host
:return: Clone spec
"""
if disk_type is not None:
@ -1011,7 +1012,7 @@ class VMwareVolumeOps(object):
else:
disk_device = None
relocate_spec = self._get_relocate_spec(datastore, None, None,
relocate_spec = self._get_relocate_spec(datastore, None, host,
disk_move_type, disk_type,
disk_device)
cf = self._session.vim.client.factory
@ -1025,7 +1026,7 @@ class VMwareVolumeOps(object):
return clone_spec
def clone_backing(self, name, backing, snapshot, clone_type, datastore,
disk_type=None):
disk_type=None, host=None):
"""Clone backing.
If the clone_type is 'full', then a full clone of the source volume
@ -1038,19 +1039,22 @@ class VMwareVolumeOps(object):
:param clone_type: Whether a full clone or linked clone is to be made
:param datastore: Reference to the datastore entity
:param disk_type: Disk type of the clone
:param host: Target host
"""
LOG.debug("Creating a clone of backing: %(back)s, named: %(name)s, "
"clone type: %(type)s from snapshot: %(snap)s on "
"datastore: %(ds)s with disk type: %(disk_type)s.",
"host: %(host)s, datastore: %(ds)s with disk type: "
"%(disk_type)s.",
{'back': backing, 'name': name, 'type': clone_type,
'snap': snapshot, 'ds': datastore, 'disk_type': disk_type})
'snap': snapshot, 'ds': datastore, 'disk_type': disk_type,
'host': host})
folder = self._get_folder(backing)
if clone_type == LINKED_CLONE_TYPE:
disk_move_type = 'createNewChildDiskBacking'
else:
disk_move_type = 'moveAllDiskBackingsAndDisallowSharing'
clone_spec = self._get_clone_spec(datastore, disk_move_type, snapshot,
backing, disk_type)
backing, disk_type, host)
task = self._session.invoke_api(self._session.vim, 'CloneVM_Task',
backing, folder=folder, name=name,
spec=clone_spec)