Hyper-V: fix image handling when shared storage is being used

The image cache operation uses thread locks, for which reason it is
prone to race conditions when using shared storage. This is avoided
by using file locks, placed on the shared path.

Change-Id: I2b002993b7ab0973960794bfdd81d4fe57bf7399
Closes-Bug: #1565895
(cherry picked from commit 8c40c4268b)
This commit is contained in:
Lucian Petrut 2016-06-16 11:41:20 +03:00 committed by Lee Yarwood
parent 82127c5053
commit d4ee75c931
2 changed files with 27 additions and 17 deletions

View File

@ -242,6 +242,13 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
self.imagecache._pathutils.get_age_of_file.assert_has_calls(calls)
mock_get_backing_files.assert_called_once_with(mock.sentinel.image)
def test_remove_old_image(self):
fake_img_path = os.path.join(self.FAKE_BASE_DIR,
self.FAKE_IMAGE_REF)
self.imagecache._remove_old_image(fake_img_path)
self.imagecache._pathutils.remove.assert_called_once_with(
fake_img_path)
@mock.patch.object(imagecache.ImageCache, '_age_and_verify_cached_images')
@mock.patch.object(imagecache.ImageCache, '_list_base_images')
@mock.patch.object(imagecache.ImageCache, '_list_running_instances')

View File

@ -37,17 +37,6 @@ LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
def synchronize_with_path(f):
def wrapper(self, image_path):
@utils.synchronized(image_path)
def inner():
return f(self, image_path)
return inner()
return wrapper
class ImageCache(imagecache.ImageCacheManager):
def __init__(self):
super(ImageCache, self).__init__()
@ -79,7 +68,11 @@ class ImageCache(imagecache.ImageCacheManager):
root_vhd_size_gb,
path_parts[1])
@utils.synchronized(resized_vhd_path)
lock_path = os.path.dirname(resized_vhd_path)
lock_name = "%s-cache.lock" % os.path.basename(resized_vhd_path)
@utils.synchronized(name=lock_name, external=True,
lock_path=lock_path)
def copy_and_resize_vhd():
if not self._pathutils.exists(resized_vhd_path):
try:
@ -109,7 +102,10 @@ class ImageCache(imagecache.ImageCacheManager):
base_vhd_dir = self._pathutils.get_base_vhd_dir()
base_vhd_path = os.path.join(base_vhd_dir, image_id)
@utils.synchronized(base_vhd_path)
lock_name = "%s-cache.lock" % image_id
@utils.synchronized(name=lock_name, external=True,
lock_path=base_vhd_dir)
def fetch_image_if_not_existing():
vhd_path = None
for format_ext in ['vhd', 'vhdx']:
@ -206,11 +202,18 @@ class ImageCache(imagecache.ImageCacheManager):
age_seconds = self._pathutils.get_age_of_file(img)
if age_seconds > max_age_seconds:
LOG.info(_LI("Removing old, unused image: %s"), img)
self.remove_old_image(img)
self._remove_old_image(img)
@synchronize_with_path
def remove_old_image(self, img):
self._pathutils.remove(img)
def _remove_old_image(self, image_path):
lock_path = os.path.dirname(image_path)
lock_name = "%s-cache.lock" % os.path.basename(image_path)
@utils.synchronized(name=lock_name, external=True,
lock_path=lock_path)
def _image_synchronized_remove():
self._pathutils.remove(image_path)
_image_synchronized_remove()
def update(self, context, all_instances):
base_vhd_dir = self._pathutils.get_base_vhd_dir()