Add image precache support
This change adds the driver method required for pre-caching Glance images. Change-Id: I9cc5d2c4533f17261f2d7facd67531014453b122 blueprint: image-precache-support
This commit is contained in:
parent
71b317276f
commit
b3636915d7
|
@ -407,6 +407,11 @@ class HyperVDriver(driver.ComputeDriver):
|
|||
def manage_image_cache(self, context, all_instances):
|
||||
self._imagecache.update(context, all_instances)
|
||||
|
||||
def cache_image(self, context, image_id):
|
||||
image_path, fetched = self._imagecache.cache_image(
|
||||
context, image_id)
|
||||
return fetched
|
||||
|
||||
def attach_interface(self, context, instance, image_meta, vif):
|
||||
self._vmops.attach_interface(context, instance, vif)
|
||||
|
||||
|
|
|
@ -102,42 +102,9 @@ class ImageCache(imagecache.ImageCacheManager):
|
|||
def get_cached_image(self, context, instance, rescue_image_id=None):
|
||||
image_id = rescue_image_id or instance.image_ref
|
||||
image_type = instance.system_metadata['image_disk_format']
|
||||
|
||||
base_image_dir = self._pathutils.get_base_vhd_dir()
|
||||
base_image_path = os.path.join(base_image_dir, image_id)
|
||||
|
||||
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']:
|
||||
test_path = base_image_path + '.' + format_ext
|
||||
if self._pathutils.exists(test_path):
|
||||
image_path = test_path
|
||||
self._update_image_timestamp(image_id)
|
||||
break
|
||||
|
||||
if not image_path:
|
||||
try:
|
||||
images.fetch(context, image_id, base_image_path,
|
||||
instance.trusted_certs)
|
||||
if image_type == 'iso':
|
||||
format_ext = 'iso'
|
||||
else:
|
||||
format_ext = self._vhdutils.get_vhd_format(
|
||||
base_image_path)
|
||||
image_path = base_image_path + '.' + format_ext.lower()
|
||||
self._pathutils.rename(base_image_path, image_path)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if self._pathutils.exists(base_image_path):
|
||||
self._pathutils.remove(base_image_path)
|
||||
|
||||
return image_path
|
||||
|
||||
image_path = fetch_image_if_not_existing()
|
||||
trusted_certs = instance.trusted_certs
|
||||
image_path, already_exists = self.cache_image(
|
||||
context, image_id, image_type, trusted_certs)
|
||||
|
||||
# Note: rescue images are not resized.
|
||||
is_vhd = image_path.split('.')[-1].lower() == 'vhd'
|
||||
|
@ -154,6 +121,50 @@ class ImageCache(imagecache.ImageCacheManager):
|
|||
|
||||
return image_path
|
||||
|
||||
def cache_image(self, context, image_id,
|
||||
image_type=None, trusted_certs=None):
|
||||
if not image_type:
|
||||
image_info = images.get_info(context, image_id)
|
||||
image_type = image_info['disk_format']
|
||||
|
||||
base_image_dir = self._pathutils.get_base_vhd_dir()
|
||||
base_image_path = os.path.join(base_image_dir, image_id)
|
||||
|
||||
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():
|
||||
fetched = False
|
||||
image_path = None
|
||||
for format_ext in ['vhd', 'vhdx', 'iso']:
|
||||
test_path = base_image_path + '.' + format_ext
|
||||
if self._pathutils.exists(test_path):
|
||||
image_path = test_path
|
||||
self._update_image_timestamp(image_id)
|
||||
break
|
||||
|
||||
if not image_path:
|
||||
try:
|
||||
images.fetch(context, image_id, base_image_path,
|
||||
trusted_certs)
|
||||
fetched = True
|
||||
if image_type == 'iso':
|
||||
format_ext = 'iso'
|
||||
else:
|
||||
format_ext = self._vhdutils.get_vhd_format(
|
||||
base_image_path)
|
||||
image_path = base_image_path + '.' + format_ext.lower()
|
||||
self._pathutils.rename(base_image_path, image_path)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
if self._pathutils.exists(base_image_path):
|
||||
self._pathutils.remove(base_image_path)
|
||||
|
||||
return image_path, fetched
|
||||
|
||||
return fetch_image_if_not_existing()
|
||||
|
||||
def _verify_rescue_image(self, instance, rescue_image_id,
|
||||
rescue_image_path):
|
||||
rescue_image_info = self._vhdutils.get_vhd_info(rescue_image_path)
|
||||
|
|
|
@ -32,7 +32,7 @@ class HyperVBaseTestCase(test.NoDBTestCase):
|
|||
utilsfactory_patcher = mock.patch.object(
|
||||
utilsfactory, '_get_class', HyperVBaseTestCase._mock_get_class)
|
||||
utilsfactory_patcher.start()
|
||||
self.addCleanup(utilsfactory_patcher.stop)
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
self._patch_autospec_classes()
|
||||
|
||||
|
|
|
@ -545,6 +545,17 @@ class HyperVDriverTestCase(test_base.HyperVBaseTestCase):
|
|||
self.driver._imagecache.update.assert_called_once_with(
|
||||
mock.sentinel.context, mock.sentinel.all_instances)
|
||||
|
||||
def test_cache_image(self):
|
||||
self.driver._imagecache.cache_image.return_value = (
|
||||
mock.sentinel.image_path, mock.sentinel.fetched)
|
||||
|
||||
fetched = self.driver.cache_image(
|
||||
mock.sentinel.context, mock.sentinel.image_id)
|
||||
|
||||
self.assertEqual(mock.sentinel.fetched, fetched)
|
||||
self.driver._imagecache.cache_image.assert_called_once_with(
|
||||
mock.sentinel.context, mock.sentinel.image_id)
|
||||
|
||||
def test_attach_interface(self):
|
||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||
self.driver.attach_interface(
|
||||
|
|
|
@ -104,6 +104,13 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
|
|||
self.imagecache._vhdutils.get_vhd_format.return_value = (
|
||||
constants.DISK_FORMAT_VHD)
|
||||
|
||||
mock.patch.object(imagecache.images, 'fetch').start()
|
||||
mock.patch.object(imagecache.images, 'get_info').start()
|
||||
|
||||
self._mock_fetch = imagecache.images.fetch
|
||||
self._mock_img_info = imagecache.images.get_info
|
||||
self._mock_img_info.return_value = dict(disk_format=image_format)
|
||||
|
||||
CONF.set_override('use_cow_images', use_cow)
|
||||
|
||||
image_file_name = rescue_image_id or self.FAKE_IMAGE_REF
|
||||
|
@ -113,31 +120,51 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
|
|||
constants.DISK_FORMAT_VHD.lower())
|
||||
return (expected_path, expected_vhd_path)
|
||||
|
||||
@mock.patch.object(imagecache.images, 'fetch')
|
||||
def test_get_cached_image_with_fetch(self, mock_fetch):
|
||||
@ddt.data({},
|
||||
{'exists': False, 'provide_img_type': False})
|
||||
@ddt.unpack
|
||||
def test_cache_image(self, exists=True, provide_img_type=True):
|
||||
(expected_path,
|
||||
expected_image_path) = self._prepare_get_cached_image(False, False)
|
||||
expected_image_path) = self._prepare_get_cached_image(
|
||||
path_exists=exists)
|
||||
img_type = constants.DISK_FORMAT_VHD if provide_img_type else None
|
||||
|
||||
ret_path, fetched = self.imagecache.cache_image(
|
||||
self.context, self.FAKE_IMAGE_REF, img_type)
|
||||
|
||||
self.assertEqual(expected_image_path, ret_path)
|
||||
self.assertEqual(not exists, fetched)
|
||||
|
||||
if not provide_img_type:
|
||||
self._mock_img_info.assert_called_once_with(
|
||||
self.context, self.FAKE_IMAGE_REF)
|
||||
|
||||
def test_get_cached_image_with_fetch(self):
|
||||
(expected_path,
|
||||
expected_image_path) = self._prepare_get_cached_image(
|
||||
path_exists=False,
|
||||
use_cow=False)
|
||||
|
||||
result = self.imagecache.get_cached_image(self.context, self.instance)
|
||||
self.assertEqual(expected_image_path, result)
|
||||
|
||||
mock_fetch.assert_called_once_with(self.context, self.FAKE_IMAGE_REF,
|
||||
expected_path,
|
||||
self.instance.trusted_certs)
|
||||
self._mock_fetch.assert_called_once_with(
|
||||
self.context, self.FAKE_IMAGE_REF,
|
||||
expected_path,
|
||||
self.instance.trusted_certs)
|
||||
self.imagecache._vhdutils.get_vhd_format.assert_called_once_with(
|
||||
expected_path)
|
||||
self.imagecache._pathutils.rename.assert_called_once_with(
|
||||
expected_path, expected_image_path)
|
||||
|
||||
@mock.patch.object(imagecache.images, 'fetch')
|
||||
def test_get_cached_image_with_fetch_exception(self, mock_fetch):
|
||||
def test_get_cached_image_with_fetch_exception(self):
|
||||
(expected_path,
|
||||
expected_image_path) = self._prepare_get_cached_image(False, False)
|
||||
|
||||
# path doesn't exist until fetched.
|
||||
self.imagecache._pathutils.exists.side_effect = [False, False, False,
|
||||
True]
|
||||
mock_fetch.side_effect = exception.InvalidImageRef(
|
||||
self._mock_fetch.side_effect = exception.InvalidImageRef(
|
||||
image_href=self.FAKE_IMAGE_REF)
|
||||
|
||||
self.assertRaises(exception.InvalidImageRef,
|
||||
|
@ -164,8 +191,7 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
|
|||
mock_update_img_timestamp.assert_called_once_with(
|
||||
self.instance.image_ref)
|
||||
|
||||
@mock.patch.object(imagecache.images, 'fetch')
|
||||
def test_cache_rescue_image_bigger_than_flavor(self, mock_fetch):
|
||||
def test_cache_rescue_image_bigger_than_flavor(self):
|
||||
fake_rescue_image_id = 'fake_rescue_image_id'
|
||||
|
||||
self.imagecache._vhdutils.get_vhd_info.return_value = {
|
||||
|
@ -179,10 +205,9 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
|
|||
self.context, self.instance,
|
||||
fake_rescue_image_id)
|
||||
|
||||
mock_fetch.assert_called_once_with(self.context,
|
||||
fake_rescue_image_id,
|
||||
expected_path,
|
||||
self.instance.trusted_certs)
|
||||
self._mock_fetch.assert_called_once_with(
|
||||
self.context, fake_rescue_image_id, expected_path,
|
||||
self.instance.trusted_certs)
|
||||
self.imagecache._vhdutils.get_vhd_info.assert_called_once_with(
|
||||
expected_vhd_path)
|
||||
|
||||
|
|
Loading…
Reference in New Issue