Merge "VMware: Improve scalability of querying volumes"

This commit is contained in:
Zuul 2017-11-13 18:04:50 +00:00 committed by Gerrit Code Review
commit a8d1557ea3
4 changed files with 190 additions and 89 deletions

View File

@ -176,7 +176,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume = self._create_volume_dict()
self._driver.delete_volume(volume)
vops.get_backing.assert_called_once_with(volume['name'])
vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
self.assertFalse(vops.delete_backing.called)
@mock.patch.object(VMDK_DRIVER, 'volumeops')
@ -187,7 +187,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume = self._create_volume_dict()
self._driver.delete_volume(volume)
vops.get_backing.assert_called_once_with(volume['name'])
vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
vops.delete_backing.assert_called_once_with(backing)
@mock.patch('cinder.volume.drivers.vmware.vmdk.'
@ -354,7 +354,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
ret = self._driver.create_snapshot(snapshot)
self.assertIsNone(ret)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
self.assertFalse(vops.create_snapshot.called)
@mock.patch.object(VMDK_DRIVER, '_in_use', return_value=False)
@ -368,7 +369,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
ret = self._driver.create_snapshot(snapshot)
self.assertIsNone(ret)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
vops.create_snapshot.assert_called_once_with(
backing, snapshot['name'], snapshot['display_description'])
@ -397,7 +399,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
ret = self._driver.create_snapshot(snapshot)
self.assertEqual(model_update, ret)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
create_snapshot_template_format.assert_called_once_with(
snapshot, backing)
@ -447,7 +450,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume=volume)
self._driver.delete_snapshot(snapshot)
vops.get_backing.assert_called_once_with(snapshot.volume_name)
vops.get_backing.assert_called_once_with(snapshot.volume_name,
snapshot.volume.id)
vops.get_snapshot.assert_not_called()
vops.delete_snapshot.assert_not_called()
@ -462,7 +466,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume=volume)
self._driver.delete_snapshot(snapshot)
vops.get_backing.assert_called_once_with(snapshot.volume_name)
vops.get_backing.assert_called_once_with(snapshot.volume_name,
snapshot.volume.id)
vops.get_snapshot.assert_called_once_with(backing, snapshot.name)
in_use.assert_called_once_with(snapshot.volume)
vops.delete_snapshot.assert_called_once_with(
@ -491,7 +496,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume=volume)
self._driver.delete_snapshot(snapshot)
vops.get_backing.assert_called_once_with(snapshot.volume_name)
vops.get_backing.assert_called_once_with(snapshot.volume_name,
snapshot.volume.id)
vops.get_snapshot.assert_called_once_with(backing, snapshot.name)
vops.delete_snapshot.assert_not_called()
@ -510,7 +516,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
provider_location=inv_path)
self._driver.delete_snapshot(snapshot)
vops.get_backing.assert_called_once_with(snapshot.volume_name)
vops.get_backing.assert_called_once_with(snapshot.volume_name,
snapshot.volume.id)
vops.get_snapshot.assert_not_called()
in_use.assert_called_once_with(snapshot.volume)
delete_snapshot_template_format.assert_called_once_with(snapshot)
@ -1162,7 +1169,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
context, volume, image_service, image_meta)
validate_disk_format.assert_called_once_with(image_meta['disk_format'])
vops.get_backing.assert_called_once_with(volume['name'])
vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
if not backing_exists:
create_backing.assert_called_once_with(volume)
vops.get_vmdk_path.assert_called_once_with(backing)
@ -1672,6 +1679,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
self.assertEqual(ds_sel_cls.return_value, self._driver._ds_sel)
vops.get_cluster_refs.assert_called_once_with(
self._driver.configuration.vmware_cluster_name)
vops.build_backing_ref_cache.assert_called_once_with()
self.assertEqual(list(cluster_refs.values()),
list(self._driver._clusters))
@ -2076,7 +2084,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
snapshot = self._create_snapshot_dict(src_vref)
self._driver.create_volume_from_snapshot(volume, snapshot)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
clone_backing.assert_not_called()
@mock.patch.object(VMDK_DRIVER, 'volumeops')
@ -2093,7 +2102,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
snapshot = self._create_snapshot_dict(src_vref)
self._driver.create_volume_from_snapshot(volume, snapshot)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
vops.get_snapshot.assert_called_once_with(backing, snapshot['name'])
clone_backing.assert_not_called()
@ -2122,7 +2132,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
src_vref, provider_location=provider_location)
self._driver.create_volume_from_snapshot(volume, snapshot)
vops.get_backing.assert_called_once_with(snapshot['volume_name'])
vops.get_backing.assert_called_once_with(snapshot['volume_name'],
snapshot['volume']['id'])
if template:
create_volume_from_template.assert_called_once_with(
volume, mock.sentinel.inv_path)
@ -2244,7 +2255,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
self._driver.create_cloned_volume(volume, src_vref)
vops.get_backing.assert_called_once_with(src_vref['name'])
vops.get_backing.assert_called_once_with(src_vref['name'],
src_vref['id'])
clone_backing.assert_not_called()
@mock.patch.object(VMDK_DRIVER, 'volumeops')
@ -2262,7 +2274,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
self._driver.create_cloned_volume(volume, src_vref)
vops.get_backing.assert_called_once_with(src_vref['name'])
vops.get_backing.assert_called_once_with(src_vref['name'],
src_vref['id'])
get_clone_type.assert_called_once_with(volume)
clone_backing.assert_called_once_with(
volume, backing, None, volumeops.FULL_CLONE_TYPE, src_vref['size'])
@ -2285,7 +2298,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
self._driver.create_cloned_volume(volume, src_vref)
vops.get_backing.assert_called_once_with(src_vref['name'])
vops.get_backing.assert_called_once_with(src_vref['name'],
src_vref['id'])
get_clone_type.assert_called_once_with(volume)
temp_snap_name = 'temp-snapshot-%s' % volume['id']
vops.create_snapshot.assert_called_once_with(
@ -2312,7 +2326,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
self._driver.create_cloned_volume,
volume,
src_vref)
vops.get_backing.assert_called_once_with(src_vref['name'])
vops.get_backing.assert_called_once_with(src_vref['name'],
src_vref['id'])
get_clone_type.assert_called_once_with(volume)
@mock.patch.object(VMDK_DRIVER, 'volumeops')
@ -2330,7 +2345,8 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
src_vref = self._create_volume_dict(vol_id=self.SRC_VOL_ID)
self._driver.create_cloned_volume(volume, src_vref)
vops.get_backing.assert_called_once_with(src_vref['name'])
vops.get_backing.assert_called_once_with(src_vref['name'],
src_vref['id'])
get_clone_type.assert_called_once_with(volume)
clone_attached_volume.assert_called_once_with(src_vref, volume)
@ -2938,7 +2954,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
volume = self._create_volume_dict()
self._driver.unmanage(volume)
vops.get_backing.assert_called_once_with(volume['name'])
vops.get_backing.assert_called_once_with(volume['name'], volume['id'])
vops.update_backing_extra_config.assert_called_once_with(
backing, {vmdk.EXTRA_CONFIG_VOLUME_ID_KEY: '',
volumeops.BACKING_UUID_KEY: ''})
@ -3052,7 +3068,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
self._driver.accept_transfer(context, volume, mock.sentinel.new_user,
new_project)
vops.get_backing.assert_called_once_with(volume.name)
vops.get_backing.assert_called_once_with(volume.name, volume.id)
vops.get_dc.assert_called_once_with(backing)
get_volume_group_folder.assert_called_once_with(dc, new_project)
vops.move_backing_to_folder.assert_called_once_with(backing,

View File

@ -65,48 +65,32 @@ class VolumeOpsTestCase(test.TestCase):
vm.propSet = [prop]
return vm
def test_get_backing(self):
name = 'mock-backing'
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'get_backing_by_uuid')
def test_get_backing(self, get_backing_by_uuid):
ref = mock.sentinel.ref
get_backing_by_uuid.return_value = ref
# Test no result
self.session.invoke_api.return_value = None
result = self.vops.get_backing(name)
self.assertIsNone(result)
self.session.invoke_api.assert_called_once_with(vim_util,
'get_objects',
self.session.vim,
'VirtualMachine',
self.MAX_OBJECTS)
name = mock.sentinel.name
backing_uuid = mock.sentinel.backing_uuid
ret = self.vops.get_backing(name, backing_uuid)
# Test single result
vm = self.vm(name)
vm.obj = mock.sentinel.vm_obj
retrieve_result = mock.Mock(spec=object)
retrieve_result.objects = [vm]
self.session.invoke_api.return_value = retrieve_result
self.vops.cancel_retrieval = mock.Mock(spec=object)
result = self.vops.get_backing(name)
self.assertEqual(mock.sentinel.vm_obj, result)
self.session.invoke_api.assert_called_with(vim_util, 'get_objects',
self.session.vim,
'VirtualMachine',
self.MAX_OBJECTS)
self.vops.cancel_retrieval.assert_called_once_with(retrieve_result)
self.assertEqual(ref, ret)
get_backing_by_uuid.assert_called_once_with(backing_uuid)
# Test multiple results
retrieve_result2 = mock.Mock(spec=object)
retrieve_result2.objects = [vm('1'), vm('2'), vm('3')]
self.session.invoke_api.return_value = retrieve_result2
self.vops.continue_retrieval = mock.Mock(spec=object)
self.vops.continue_retrieval.return_value = retrieve_result
result = self.vops.get_backing(name)
self.assertEqual(mock.sentinel.vm_obj, result)
self.session.invoke_api.assert_called_with(vim_util, 'get_objects',
self.session.vim,
'VirtualMachine',
self.MAX_OBJECTS)
self.vops.continue_retrieval.assert_called_once_with(retrieve_result2)
self.vops.cancel_retrieval.assert_called_with(retrieve_result)
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'get_backing_by_uuid')
def test_get_backing_legacy(self, get_backing_by_uuid):
ref = mock.sentinel.ref
get_backing_by_uuid.return_value = None
name = mock.sentinel.name
self.vops._backing_ref_cache[name] = ref
backing_uuid = mock.sentinel.backing_uuid
ret = self.vops.get_backing(name, backing_uuid)
self.assertEqual(ref, ret)
get_backing_by_uuid.assert_called_once_with(backing_uuid)
def test_get_backing_by_uuid(self):
backing = mock.sentinel.backing
@ -122,6 +106,65 @@ class VolumeOpsTestCase(test.TestCase):
vmSearch=True,
instanceUuid=True)
def _create_property(self, name, val):
prop = mock.Mock()
prop.name = name
prop.val = val
return prop
def _create_backing_obj(self, name, ref, instance_uuid=None, vol_id=None):
name_prop = self._create_property('name', name)
instance_uuid_prop = self._create_property('config.instanceUuid',
instance_uuid)
vol_id_val = mock.Mock(value=vol_id)
vol_id_prop = self._create_property(
'config.extraConfig["cinder.volume.id"]', vol_id_val)
backing = mock.Mock()
backing.obj = ref
backing.propSet = [name_prop, instance_uuid_prop, vol_id_prop]
return backing
@mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.'
'continue_retrieval', return_value=None)
def test_build_backing_ref_cache(self, continue_retrieval):
uuid1 = 'd68cbee0-c1f7-4886-98a4-cf2201461c6e'
ref1 = mock.sentinel.ref1
non_vol_backing = self._create_backing_obj(
'foo', ref1, instance_uuid=uuid1)
uuid2 = 'f36f0e87-97e0-4a1c-b788-2f84f1376960'
ref2 = mock.sentinel.ref2
legacy_vol_backing = self._create_backing_obj(
'volume-f36f0e87-97e0-4a1c-b788-2f84f1376960', ref2,
instance_uuid=uuid2)
uuid3 = '405d6afd-43be-4ce0-9e5f-fd49559e2763'
ref3 = mock.sentinel.ref3
vol_backing = self._create_backing_obj(
'volume-405d6afd-43be-4ce0-9e5f-fd49559e2763', ref3,
instance_uuid=uuid3, vol_id=uuid3)
result = mock.Mock(objects=[
non_vol_backing, legacy_vol_backing, vol_backing])
self.session.invoke_api.return_value = result
self.vops.build_backing_ref_cache()
exp_cache = {'foo': ref1,
'volume-f36f0e87-97e0-4a1c-b788-2f84f1376960': ref2}
self.assertEqual(exp_cache, self.vops._backing_ref_cache)
self.session.invoke_api.assert_called_once_with(
vim_util,
'get_objects',
self.session.vim,
'VirtualMachine',
self.MAX_OBJECTS,
properties_to_collect=[
'name',
'config.instanceUuid',
'config.extraConfig["cinder.volume.id"]'])
continue_retrieval.assert_called_once_with(result)
def test_delete_backing(self):
backing = mock.sentinel.backing
task = mock.sentinel.task

View File

@ -351,7 +351,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
:param volume: Volume object
"""
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'], volume['id'])
if not backing:
LOG.info("Backing not available, no operation "
"to be performed.")
@ -604,7 +604,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
:param connector: Connector information
:return: Return connection information
"""
backing = self.volumeops.get_backing(volume.name)
backing = self.volumeops.get_backing(volume.name, volume.id)
if 'instance' in connector:
# The instance exists
instance = vim_util.get_moref(connector['instance'],
@ -714,7 +714,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
LOG.error(msg)
raise exception.InvalidVolume(msg)
backing = self.volumeops.get_backing(snapshot['volume_name'])
backing = self.volumeops.get_backing(snapshot['volume_name'],
volume['id'])
if not backing:
LOG.info("There is no backing, so will not create "
"snapshot: %s.", snapshot['name'])
@ -762,7 +763,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
inv_path = snapshot.provider_location
is_template = inv_path is not None
backing = self.volumeops.get_backing(snapshot.volume_name)
backing = self.volumeops.get_backing(snapshot.volume_name,
snapshot.volume.id)
if not backing:
LOG.debug("Backing does not exist for volume.",
resource=snapshot.volume)
@ -1234,7 +1236,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
LOG.exception("Error occurred while copying image: %(id)s "
"to volume: %(vol)s.",
{'id': image_id, 'vol': volume['name']})
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'],
volume['id'])
if backing:
# delete the backing
self.volumeops.delete_backing(backing)
@ -1319,7 +1322,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
# If the user-specified volume size is greater than backing's
# current disk size, we should extend the disk.
volume_size = volume['size'] * units.Gi
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'], volume['id'])
disk_size = self.volumeops.get_disk_size(backing)
if volume_size > disk_size:
LOG.debug("Extending volume: %(name)s since the user specified "
@ -1355,7 +1358,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
VMwareVcVmdkDriver._validate_disk_format(image_meta['disk_format'])
# get backing vm of volume and its vmdk path
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'], volume['id'])
if not backing:
LOG.info("Backing not found, creating for volume: %s",
volume['name'])
@ -1415,7 +1418,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
return False
# If the backing doesn't exist, retype is NOP.
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'], volume['id'])
if backing is None:
LOG.debug("Backing for volume: %s doesn't exist; retype is NOP.",
volume['name'])
@ -1570,7 +1573,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
:param new_size: new size in GB to extend this volume to
"""
vol_name = volume['name']
backing = self.volumeops.get_backing(vol_name)
backing = self.volumeops.get_backing(vol_name, volume['id'])
if not backing:
LOG.info("There is no backing for volume: %s; no need to "
"extend the virtual disk.", vol_name)
@ -1701,7 +1704,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
self._manage_existing_int(volume, vm, disk)
def unmanage(self, volume):
backing = self.volumeops.get_backing(volume['name'])
backing = self.volumeops.get_backing(volume['name'], volume['id'])
if backing:
extra_config = self._get_extra_config(volume)
for key in extra_config:
@ -1822,6 +1825,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
cluster_names).values()
LOG.info("Using compute cluster(s): %s.", cluster_names)
self.volumeops.build_backing_ref_cache()
LOG.info("Successfully setup driver: %(driver)s for server: "
"%(ip)s.", {'driver': self.__class__.__name__,
'ip': self.configuration.vmware_host_ip})
@ -2011,7 +2016,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
:param volume: New Volume object
:param snapshot: Reference to snapshot entity
"""
backing = self.volumeops.get_backing(snapshot['volume_name'])
backing = self.volumeops.get_backing(snapshot['volume_name'],
snapshot['volume']['id'])
if not backing:
LOG.info("There is no backing for the snapshotted volume: "
"%(snap)s. Not creating any backing for the "
@ -2093,7 +2099,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
:param volume: New Volume object
:param src_vref: Source Volume object
"""
backing = self.volumeops.get_backing(src_vref['name'])
backing = self.volumeops.get_backing(src_vref['name'], src_vref['id'])
if not backing:
LOG.info("There is no backing for the source volume: %(src)s. "
"Not creating any backing for volume: %(vol)s.",
@ -2140,7 +2146,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
def accept_transfer(self, context, volume, new_user, new_project):
"""Accept the transfer of a volume for a new user/project."""
backing = self.volumeops.get_backing(volume.name)
backing = self.volumeops.get_backing(volume.name, volume.id)
if backing:
dc = self.volumeops.get_dc(backing)
new_folder = self._get_volume_group_folder(dc, new_project)

View File

@ -17,7 +17,6 @@
Implements operations on volumes residing on VMware datastores.
"""
from oslo_log import log as logging
from oslo_utils import units
from oslo_vmware import exceptions
@ -286,31 +285,29 @@ class VMwareVolumeOps(object):
self._extension_key = extension_key
self._extension_type = extension_type
self._folder_cache = {}
self._backing_ref_cache = {}
def get_backing(self, name):
"""Get the backing based on name.
def get_backing(self, name, backing_uuid):
"""Get the backing based on name or uuid.
:param name: Name of the backing
:param backing_uuid: UUID of the backing
:return: Managed object reference to the backing
"""
retrieve_result = self._session.invoke_api(vim_util, 'get_objects',
self._session.vim,
'VirtualMachine',
self._max_objects)
while retrieve_result:
vms = retrieve_result.objects
for vm in vms:
if vm.propSet[0].val == name:
# We got the result, so cancel further retrieval.
self.cancel_retrieval(retrieve_result)
return vm.obj
# Result not obtained, continue retrieving results.
retrieve_result = self.continue_retrieval(retrieve_result)
ref = self.get_backing_by_uuid(backing_uuid)
if not ref:
# old version of the driver might have created this backing and
# hence cannot be queried by uuid
LOG.debug("Returning cached ref for %s.", name)
ref = self._backing_ref_cache.get(name)
LOG.debug("Did not find any backing with name: %s", name)
LOG.debug("Backing (%(name)s, %(uuid)s) ref: %(ref)s.",
{'name': name, 'uuid': backing_uuid, 'ref': ref})
return ref
def get_backing_by_uuid(self, uuid):
LOG.debug("Get ref by UUID: %s.", uuid)
result = self._session.invoke_api(
self._session.vim,
'FindAllByUuid',
@ -321,6 +318,45 @@ class VMwareVolumeOps(object):
if result:
return result[0]
def build_backing_ref_cache(self, name_regex=None):
LOG.debug("Building backing ref cache.")
result = self._session.invoke_api(
vim_util,
'get_objects',
self._session.vim,
'VirtualMachine',
self._max_objects,
properties_to_collect=[
'name',
'config.instanceUuid',
'config.extraConfig["cinder.volume.id"]'])
while result:
for backing in result.objects:
instance_uuid = None
vol_id = None
for prop in backing.propSet:
if prop.name == 'name':
name = prop.val
elif prop.name == 'config.instanceUuid':
instance_uuid = prop.val
else:
vol_id = prop.val.value
if name_regex and not name_regex.match(name):
continue
if instance_uuid and instance_uuid == vol_id:
# no need to cache backing with UUID set to volume ID
continue
self._backing_ref_cache[name] = backing.obj
result = self.continue_retrieval(result)
LOG.debug("Backing ref cache size: %d.", len(self._backing_ref_cache))
def delete_backing(self, backing):
"""Delete the backing.