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 Partial-Bug: #1565895
This commit is contained in:
parent
94558ef556
commit
7c2e267d5d
|
@ -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__()
|
||||
|
@ -82,7 +71,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:
|
||||
|
@ -113,7 +106,10 @@ class ImageCache(imagecache.ImageCacheManager):
|
|||
base_image_dir = self._pathutils.get_base_vhd_dir()
|
||||
base_image_path = os.path.join(base_image_dir, image_id)
|
||||
|
||||
@utils.synchronized(base_image_path)
|
||||
lock_name = "%s-cache.lock" % image_id
|
||||
|
||||
@utils.synchronized(name=lock_name, external=True,
|
||||
lock_path=base_image_dir)
|
||||
def fetch_image_if_not_existing():
|
||||
image_path = None
|
||||
for format_ext in ['vhd', 'vhdx', 'iso']:
|
||||
|
@ -214,11 +210,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()
|
||||
|
|
|
@ -237,6 +237,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')
|
||||
|
|
Loading…
Reference in New Issue