Merge "Guard against missing image cache directory" into stable/train
This commit is contained in:
commit
64ec4198c0
|
@ -674,12 +674,12 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
|
||||||
lock_path=lock_path)
|
lock_path=lock_path)
|
||||||
|
|
||||||
@mock.patch('os.stat')
|
@mock.patch('os.stat')
|
||||||
def test_cache_dir_is_on_same_dev_as_instances_dir(self, mock_stat):
|
def test_get_disk_usage_cache_is_on_different_disk(self, mock_stat):
|
||||||
mock_stat.side_effect = [mock.Mock(st_dev=0), mock.Mock(st_dev=1)]
|
mock_stat.side_effect = [mock.Mock(st_dev=0), mock.Mock(st_dev=1)]
|
||||||
|
|
||||||
manager = imagecache.ImageCacheManager()
|
manager = imagecache.ImageCacheManager()
|
||||||
|
|
||||||
self.assertFalse(manager.cache_dir_is_on_same_dev_as_instances_dir)
|
self.assertEqual(0, manager.get_disk_usage())
|
||||||
|
|
||||||
mock_stat.assert_has_calls([
|
mock_stat.assert_has_calls([
|
||||||
mock.call(CONF.instances_path),
|
mock.call(CONF.instances_path),
|
||||||
mock.call(
|
mock.call(
|
||||||
|
@ -687,37 +687,34 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
|
||||||
CONF.instances_path,
|
CONF.instances_path,
|
||||||
CONF.image_cache_subdirectory_name))])
|
CONF.image_cache_subdirectory_name))])
|
||||||
|
|
||||||
mock_stat.reset_mock()
|
|
||||||
mock_stat.side_effect = [mock.Mock(st_dev=0), mock.Mock(st_dev=0)]
|
|
||||||
self.assertTrue(manager.cache_dir_is_on_same_dev_as_instances_dir)
|
|
||||||
|
|
||||||
@mock.patch('nova.virt.libvirt.imagecache.ImageCacheManager.'
|
|
||||||
'cache_dir_is_on_same_dev_as_instances_dir',
|
|
||||||
new=mock.PropertyMock(return_value=False))
|
|
||||||
def test_get_disk_usage_cache_is_on_different_disk(self):
|
|
||||||
manager = imagecache.ImageCacheManager()
|
|
||||||
|
|
||||||
self.assertEqual(0, manager.get_disk_usage())
|
|
||||||
|
|
||||||
@mock.patch('os.listdir')
|
@mock.patch('os.listdir')
|
||||||
@mock.patch('nova.virt.libvirt.imagecache.ImageCacheManager.'
|
@mock.patch('os.stat')
|
||||||
'cache_dir_is_on_same_dev_as_instances_dir',
|
def test_get_disk_usage_empty(self, mock_stat, mock_listdir):
|
||||||
new=mock.PropertyMock(return_value=True))
|
mock_stat.side_effect = [mock.Mock(st_dev=0), mock.Mock(st_dev=0)]
|
||||||
def test_get_disk_usage_empty(self, mock_listdir):
|
|
||||||
mock_listdir.return_value = []
|
mock_listdir.return_value = []
|
||||||
|
|
||||||
manager = imagecache.ImageCacheManager()
|
manager = imagecache.ImageCacheManager()
|
||||||
|
|
||||||
self.assertEqual(0, manager.get_disk_usage())
|
self.assertEqual(0, manager.get_disk_usage())
|
||||||
|
|
||||||
|
@mock.patch('os.stat')
|
||||||
|
@mock.patch('os.listdir')
|
||||||
|
def test_get_disk_usage_cache_dir_missing(self, mock_listdir, mock_stat):
|
||||||
|
mock_stat.side_effect = [mock.Mock(st_dev=0), OSError]
|
||||||
|
|
||||||
|
manager = imagecache.ImageCacheManager()
|
||||||
|
|
||||||
|
self.assertEqual(0, manager.get_disk_usage())
|
||||||
|
mock_listdir.assert_not_called()
|
||||||
|
|
||||||
@mock.patch('os.path.isfile')
|
@mock.patch('os.path.isfile')
|
||||||
@mock.patch('os.listdir')
|
@mock.patch('os.listdir')
|
||||||
@mock.patch('os.stat')
|
@mock.patch('os.stat')
|
||||||
@mock.patch('nova.virt.libvirt.imagecache.ImageCacheManager.'
|
|
||||||
'cache_dir_is_on_same_dev_as_instances_dir',
|
|
||||||
new=mock.PropertyMock(return_value=True))
|
|
||||||
def test_get_disk_usage(self, mock_stat, mock_listdir, mock_isfile):
|
def test_get_disk_usage(self, mock_stat, mock_listdir, mock_isfile):
|
||||||
mock_stat.side_effect = [
|
mock_stat.side_effect = [
|
||||||
|
# cache and instances dirs are on the same dev
|
||||||
|
mock.Mock(st_dev=0),
|
||||||
|
mock.Mock(st_dev=0),
|
||||||
# stat calls on each file in the cache directory to get the size
|
# stat calls on each file in the cache directory to get the size
|
||||||
mock.Mock(st_blocks=10), # foo
|
mock.Mock(st_blocks=10), # foo
|
||||||
mock.Mock(st_blocks=20), # bar
|
mock.Mock(st_blocks=20), # bar
|
||||||
|
@ -733,3 +730,27 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
|
||||||
|
|
||||||
# size is calculated from as st_blocks * 512
|
# size is calculated from as st_blocks * 512
|
||||||
self.assertEqual(10 * 512 + 20 * 512, manager.get_disk_usage())
|
self.assertEqual(10 * 512 + 20 * 512, manager.get_disk_usage())
|
||||||
|
|
||||||
|
@mock.patch('os.path.isfile')
|
||||||
|
@mock.patch('os.listdir')
|
||||||
|
@mock.patch('os.stat')
|
||||||
|
def test_get_disk_usage_io_error_while_reading(
|
||||||
|
self, mock_stat, mock_listdir, mock_isfile):
|
||||||
|
mock_stat.side_effect = [
|
||||||
|
# cache and instances dirs are on the same dev
|
||||||
|
mock.Mock(st_dev=0),
|
||||||
|
mock.Mock(st_dev=0),
|
||||||
|
# stat calls on each file in the cache directory to get the size
|
||||||
|
mock.Mock(st_blocks=10), # foo
|
||||||
|
OSError, # error while checking bar
|
||||||
|
]
|
||||||
|
mock_listdir.return_value = ['foo', 'bar', 'some-dir']
|
||||||
|
mock_isfile.side_effect = [
|
||||||
|
True, # foo
|
||||||
|
True, # bar
|
||||||
|
False, # some-dir
|
||||||
|
]
|
||||||
|
|
||||||
|
manager = imagecache.ImageCacheManager()
|
||||||
|
|
||||||
|
self.assertEqual(0, manager.get_disk_usage())
|
||||||
|
|
|
@ -355,26 +355,32 @@ class ImageCacheManager(imagecache.ImageCacheManager):
|
||||||
self._age_and_verify_swap_images(context, base_dir)
|
self._age_and_verify_swap_images(context, base_dir)
|
||||||
|
|
||||||
def get_disk_usage(self):
|
def get_disk_usage(self):
|
||||||
if not self.cache_dir_is_on_same_dev_as_instances_dir:
|
try:
|
||||||
return 0
|
# If the cache is on a different device than the instance dir then
|
||||||
|
# it does not consume the same disk resource as instances.
|
||||||
|
# NOTE(gibi): this does not work on Windows properly as st_dev is
|
||||||
|
# always 0
|
||||||
|
if (os.stat(CONF.instances_path).st_dev !=
|
||||||
|
os.stat(self.cache_dir).st_dev):
|
||||||
|
return 0
|
||||||
|
|
||||||
# NOTE(gibi): we need to use the disk size occupied from the file
|
# NOTE(gibi): we need to use the disk size occupied from the file
|
||||||
# system as images in the cache will not grow to their virtual size.
|
# system as images in the cache will not grow to their virtual
|
||||||
# NOTE(gibi): st.blocks is always measured in 512 byte blocks see man
|
# size.
|
||||||
# fstat
|
# NOTE(gibi): st.blocks is always measured in 512 byte blocks see
|
||||||
return sum(
|
# man fstat
|
||||||
os.stat(os.path.join(self.cache_dir, f)).st_blocks * 512
|
return sum(
|
||||||
for f in os.listdir(self.cache_dir)
|
os.stat(os.path.join(self.cache_dir, f)).st_blocks * 512
|
||||||
if os.path.isfile(os.path.join(self.cache_dir, f)))
|
for f in os.listdir(self.cache_dir)
|
||||||
|
if os.path.isfile(os.path.join(self.cache_dir, f)))
|
||||||
|
except OSError:
|
||||||
|
# NOTE(gibi): An error here can mean many things. E.g. the cache
|
||||||
|
# dir does not exists yet, the cache dir is deleted between the
|
||||||
|
# listdir() and the stat() calls, or a file is deleted between
|
||||||
|
# the listdir() and the stat() calls.
|
||||||
|
return 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cache_dir(self):
|
def cache_dir(self):
|
||||||
return os.path.join(
|
return os.path.join(
|
||||||
CONF.instances_path, CONF.image_cache_subdirectory_name)
|
CONF.instances_path, CONF.image_cache_subdirectory_name)
|
||||||
|
|
||||||
@property
|
|
||||||
def cache_dir_is_on_same_dev_as_instances_dir(self):
|
|
||||||
# NOTE(gibi): this does not work on Windows properly as st_dev is
|
|
||||||
# always 0
|
|
||||||
return (os.stat(CONF.instances_path).st_dev ==
|
|
||||||
os.stat(self.cache_dir).st_dev)
|
|
||||||
|
|
Loading…
Reference in New Issue