Image cache: move all of the variables to a common place

In addition to this it creates a base ImageCacheManager class.

This moves common image cache config from the libvirt driver into
a place that can be shared between drivers, nova.virt.imagecache
The libvirt specific image caching variables are untouched.

The other image cache fields that were in the nova.compute.manager
have also been moved to the common place.

These common configuration can be used by the
blueprint multiple-image-cache-handlers and the
blueprint vmware-image-cache-management.

DocImpact
    The following configuration variables are now in the DEFAULT
    section:
        image_cache_manager_interval
        image_cache_subdirectory_name
        remove_unused_base_images
        remove_unused_original_minimum_age_seconds

Change-Id: I92080f20c3d21ec348aff7d0a9795e773bcc2649
This commit is contained in:
Gary Kotton 2013-12-04 05:36:21 -08:00
parent 0a2ab98491
commit 6e6b4ad411
11 changed files with 333 additions and 183 deletions

View File

@ -669,11 +669,6 @@
# where instances are stored on disk (string value)
#instances_path=$state_path/instances
# Where cached images are stored under $instances_path.This is
# NOT the full path - just a folder name.For per-compute-host
# cached images, set to _base_$my_ip (string value)
#image_cache_subdirectory_name=_base
# Generate periodic compute.instance.exists notifications
# (boolean value)
#instance_usage_audit=false
@ -705,9 +700,9 @@
# updates (integer value)
#heal_instance_info_cache_interval=60
# Number of seconds to wait between runs of the image cache
# manager (integer value)
#image_cache_manager_interval=2400
# Interval in seconds for querying the host status (integer
# value)
#host_state_interval=120
# Interval in seconds for reclaiming deleted instances
# (integer value)
@ -1946,6 +1941,27 @@
#allow_same_net_traffic=true
#
# Options defined in nova.virt.imagecache
#
# Number of seconds to wait between runs of the image cache
# manager (integer value)
#image_cache_manager_interval=2400
# Where cached images are stored under $instances_path. This
# is NOT the full path - just a folder name. For per-compute-
# host cached images, set to _base_$my_ip (string value)
#image_cache_subdirectory_name=_base
# Should unused base images be removed? (boolean value)
#remove_unused_base_images=true
# Unused unresized base images younger than this will not be
# removed (integer value)
#remove_unused_original_minimum_age_seconds=86400
#
# Options defined in nova.virt.images
#
@ -2600,9 +2616,6 @@
# locations (string value)
#image_info_filename_pattern=$instances_path/$image_cache_subdirectory_name/%(image)s.info
# Should unused base images be removed? (boolean value)
#remove_unused_base_images=true
# Should unused kernel images be removed? This is only safe to
# enable if all compute nodes have been updated to support
# this option. This will be enabled by default in future.
@ -2613,10 +2626,6 @@
# removed (integer value)
#remove_unused_resized_minimum_age_seconds=3600
# Unused unresized base images younger than this will not be
# removed (integer value)
#remove_unused_original_minimum_age_seconds=86400
# Write a checksum for files in _base to disk (boolean value)
#checksum_base_images=false

View File

@ -102,12 +102,6 @@ compute_opts = [
cfg.StrOpt('instances_path',
default=paths.state_path_def('instances'),
help='where instances are stored on disk'),
cfg.StrOpt('image_cache_subdirectory_name',
default='_base',
help="Where cached images are stored under $instances_path."
"This is NOT the full path - just a folder name."
"For per-compute-host cached images, set to _base_$my_ip",
deprecated_name='base_dir_name'),
cfg.BoolOpt('instance_usage_audit',
default=False,
help="Generate periodic compute.instance.exists notifications"),
@ -135,10 +129,9 @@ interval_opts = [
default=60,
help="Number of seconds between instance info_cache self "
"healing updates"),
cfg.IntOpt("image_cache_manager_interval",
default=2400,
help='Number of seconds to wait between runs of the image '
'cache manager'),
cfg.IntOpt('host_state_interval',
default=120,
help='Interval in seconds for querying the host status'),
cfg.IntOpt('reclaim_instance_interval',
default=0,
help='Interval in seconds for reclaiming deleted instances'),
@ -217,6 +210,8 @@ CONF.import_opt('my_ip', 'nova.netconf')
CONF.import_opt('vnc_enabled', 'nova.vnc')
CONF.import_opt('enabled', 'nova.spice', group='spice')
CONF.import_opt('enable', 'nova.cells.opts', group='cells')
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
CONF.import_opt('image_cache_manager_interval', 'nova.virt.imagecache')
LOG = logging.getLogger(__name__)

View File

@ -25,7 +25,6 @@ import time
from oslo.config import cfg
from nova.compute import vm_states
from nova import conductor
from nova import db
from nova.openstack.common import importutils
@ -162,72 +161,6 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
'10737418240')
self.assertNotIn(unexpected, image_cache_manager.originals)
def test_list_running_instances(self):
all_instances = [{'image_ref': '1',
'host': CONF.host,
'name': 'inst-1',
'uuid': '123',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'host': CONF.host,
'name': 'inst-2',
'uuid': '456',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'kernel_id': '21',
'ramdisk_id': '22',
'host': 'remotehost',
'name': 'inst-3',
'uuid': '789',
'vm_state': '',
'task_state': ''}]
image_cache_manager = imagecache.ImageCacheManager()
# The argument here should be a context, but it's mocked out
image_cache_manager._list_running_instances(None, all_instances)
self.assertEqual(len(image_cache_manager.used_images), 4)
self.assertTrue(image_cache_manager.used_images['1'] ==
(1, 0, ['inst-1']))
self.assertTrue(image_cache_manager.used_images['2'] ==
(1, 1, ['inst-2', 'inst-3']))
self.assertTrue(image_cache_manager.used_images['21'] ==
(0, 1, ['inst-3']))
self.assertTrue(image_cache_manager.used_images['22'] ==
(0, 1, ['inst-3']))
self.assertIn('inst-1', image_cache_manager.instance_names)
self.assertIn('123', image_cache_manager.instance_names)
self.assertEqual(len(image_cache_manager.image_popularity), 4)
self.assertEqual(image_cache_manager.image_popularity['1'], 1)
self.assertEqual(image_cache_manager.image_popularity['2'], 2)
self.assertEqual(image_cache_manager.image_popularity['21'], 1)
self.assertEqual(image_cache_manager.image_popularity['22'], 1)
def test_list_resizing_instances(self):
all_instances = [{'image_ref': '1',
'host': CONF.host,
'name': 'inst-1',
'uuid': '123',
'vm_state': vm_states.RESIZED,
'task_state': None}]
image_cache_manager = imagecache.ImageCacheManager()
image_cache_manager._list_running_instances(None, all_instances)
self.assertEqual(len(image_cache_manager.used_images), 1)
self.assertTrue(image_cache_manager.used_images['1'] ==
(1, 0, ['inst-1']))
self.assertTrue(image_cache_manager.instance_names ==
set(['inst-1', '123', 'inst-1_resize', '123_resize']))
self.assertEqual(len(image_cache_manager.image_popularity), 1)
self.assertEqual(image_cache_manager.image_popularity['1'], 1)
def test_list_backing_images_small(self):
self.stubs.Set(os, 'listdir',
lambda x: ['_base', 'instance-00000001',
@ -572,8 +505,6 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
self.flags(instances_path='/instance_path',
image_cache_subdirectory_name='_base')
self.flags(remove_unused_base_images=True,
group='libvirt')
base_file_list = ['00000001',
'ephemeral_0_20_None',
@ -719,12 +650,15 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
# And finally we can make the call we're actually testing...
# The argument here should be a context, but it is mocked out
image_cache_manager.verify_base_images(None, all_instances)
image_cache_manager.update(None, all_instances)
# Verify
active = [fq_path(hashed_1), fq_path('%s_5368709120' % hashed_1),
fq_path(hashed_21), fq_path(hashed_22)]
self.assertEqual(image_cache_manager.active_base_files, active)
for act in active:
self.assertIn(act, image_cache_manager.active_base_files)
self.assertEqual(len(image_cache_manager.active_base_files),
len(active))
for rem in [fq_path('e97222e91fc4241f49a7f520d1dcf446751129b3_sm'),
fq_path('e09c675c2d1cfac32dae3c2d83689c8c94bc693b_sm'),
@ -738,7 +672,7 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
def test_verify_base_images_no_base(self):
self.flags(instances_path='/tmp/no/such/dir/name/please')
image_cache_manager = imagecache.ImageCacheManager()
image_cache_manager.verify_base_images(None, [])
image_cache_manager.update(None, [])
def test_is_valid_info_file(self):
hashed = 'e97222e91fc4241f49a7f520d1dcf446751129b3'
@ -762,7 +696,6 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
self.flags(instances_path=tmpdir)
self.flags(image_info_filename_pattern=('$instances_path/'
'%(image)s.info'),
remove_unused_base_images=True,
group='libvirt')
# Ensure there is a base directory
@ -797,7 +730,7 @@ class ImageCacheManagerTestCase(test.NoDBTestCase):
os.utime(base_filename + '.info', (old, old))
image_cache_manager = imagecache.ImageCacheManager()
image_cache_manager.verify_base_images(None, all_instances)
image_cache_manager.update(None, all_instances)
self.assertTrue(os.path.exists(base_filename))
self.assertTrue(os.path.exists(base_filename + '.info'))

View File

@ -85,7 +85,7 @@ CONF = cfg.CONF
CONF.import_opt('compute_manager', 'nova.service')
CONF.import_opt('host', 'nova.netconf')
CONF.import_opt('my_ip', 'nova.netconf')
CONF.import_opt('image_cache_subdirectory_name', 'nova.compute.manager')
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
CONF.import_opt('instances_path', 'nova.compute.manager')
_fake_network_info = fake_network.fake_get_instance_nw_info

View File

@ -0,0 +1,111 @@
# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.config import cfg
from nova.compute import vm_states
from nova import test
from nova.virt import imagecache
CONF = cfg.CONF
class ImageCacheManagerTests(test.NoDBTestCase):
def test_configurationi_defaults(self):
self.assertEqual(CONF.image_cache_manager_interval,
2400)
self.assertEqual(CONF.image_cache_subdirectory_name,
'_base')
self.assertTrue(CONF.remove_unused_base_images)
self.assertEqual(CONF.remove_unused_original_minimum_age_seconds,
24 * 3600)
def test_cache_manager(self):
cache_manager = imagecache.ImageCacheManager()
self.assertTrue(cache_manager.remove_unused_base_images)
self.assertRaises(NotImplementedError,
cache_manager.update, None, [])
self.assertRaises(NotImplementedError,
cache_manager._get_base)
base_images = cache_manager._list_base_images(None)
self.assertEqual(base_images['unexplained_images'], [])
self.assertEqual(base_images['originals'], [])
self.assertRaises(NotImplementedError,
cache_manager._age_and_verify_cached_images,
None, [], None)
def test_list_running_instances(self):
all_instances = [{'image_ref': '1',
'host': CONF.host,
'name': 'inst-1',
'uuid': '123',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'host': CONF.host,
'name': 'inst-2',
'uuid': '456',
'vm_state': '',
'task_state': ''},
{'image_ref': '2',
'kernel_id': '21',
'ramdisk_id': '22',
'host': 'remotehost',
'name': 'inst-3',
'uuid': '789',
'vm_state': '',
'task_state': ''}]
image_cache_manager = imagecache.ImageCacheManager()
# The argument here should be a context, but it's mocked out
running = image_cache_manager._list_running_instances(None,
all_instances)
self.assertEqual(len(running['used_images']), 4)
self.assertTrue(running['used_images']['1'] == (1, 0, ['inst-1']))
self.assertTrue(running['used_images']['2'] == (1, 1, ['inst-2',
'inst-3']))
self.assertTrue(running['used_images']['21'] == (0, 1, ['inst-3']))
self.assertTrue(running['used_images']['22'] == (0, 1, ['inst-3']))
self.assertIn('inst-1', running['instance_names'])
self.assertIn('123', running['instance_names'])
self.assertEqual(len(running['image_popularity']), 4)
self.assertEqual(running['image_popularity']['1'], 1)
self.assertEqual(running['image_popularity']['2'], 2)
self.assertEqual(running['image_popularity']['21'], 1)
self.assertEqual(running['image_popularity']['22'], 1)
def test_list_resizing_instances(self):
all_instances = [{'image_ref': '1',
'host': CONF.host,
'name': 'inst-1',
'uuid': '123',
'vm_state': vm_states.RESIZED,
'task_state': None}]
image_cache_manager = imagecache.ImageCacheManager()
running = image_cache_manager._list_running_instances(None,
all_instances)
self.assertEqual(len(running['used_images']), 1)
self.assertTrue(running['used_images']['1'] == (1, 0, ['inst-1']))
self.assertTrue(running['instance_names'] ==
set(['inst-1', '123', 'inst-1_resize', '123_resize']))
self.assertEqual(len(running['image_popularity']), 1)
self.assertEqual(running['image_popularity']['1'], 1)

View File

@ -497,7 +497,7 @@ class BareMetalDriver(driver.ComputeDriver):
def manage_image_cache(self, context, all_instances):
"""Manage the local cache of images."""
self.image_cache_manager.verify_base_images(context, all_instances)
self.image_cache_manager.update(context, all_instances)
def get_console_output(self, context, instance):
node = _get_baremetal_node_by_instance_uuid(instance.uuid)

135
nova/virt/imagecache.py Normal file
View File

@ -0,0 +1,135 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo.config import cfg
from nova.compute import task_states
from nova.compute import vm_states
imagecache_opts = [
cfg.IntOpt('image_cache_manager_interval',
default=2400,
help='Number of seconds to wait between runs of the image '
'cache manager'),
cfg.StrOpt('image_cache_subdirectory_name',
default='_base',
help='Where cached images are stored under $instances_path. '
'This is NOT the full path - just a folder name. '
'For per-compute-host cached images, set to _base_$my_ip',
deprecated_name='base_dir_name'),
cfg.BoolOpt('remove_unused_base_images',
default=True,
help='Should unused base images be removed?',
deprecated_group='libvirt'),
cfg.IntOpt('remove_unused_original_minimum_age_seconds',
default=(24 * 3600),
help='Unused unresized base images younger than this will not '
'be removed',
deprecated_group='libvirt'),
]
CONF = cfg.CONF
CONF.register_opts(imagecache_opts)
CONF.import_opt('host', 'nova.netconf')
class ImageCacheManager(object):
"""Base class for the image cache manager.
This class will provide a generic interface to the image cache manager.
"""
def __init__(self):
self.remove_unused_base_images = CONF.remove_unused_base_images
self.resize_states = [task_states.RESIZE_PREP,
task_states.RESIZE_MIGRATING,
task_states.RESIZE_MIGRATED,
task_states.RESIZE_FINISH]
def _get_base(self):
"""Returns the base directory of the cached images."""
raise NotImplementedError()
def _list_running_instances(self, context, all_instances):
"""List running instances (on all compute nodes).
This method returns a dictionary with the following keys:
- used_images
- image_popularity
- instance_names
"""
used_images = {}
image_popularity = {}
instance_names = set()
for instance in all_instances:
# NOTE(mikal): "instance name" here means "the name of a directory
# which might contain an instance" and therefore needs to include
# historical permutations as well as the current one.
instance_names.add(instance['name'])
instance_names.add(instance['uuid'])
if (instance['task_state'] in self.resize_states or
instance['vm_state'] == vm_states.RESIZED):
instance_names.add(instance['name'] + '_resize')
instance_names.add(instance['uuid'] + '_resize')
for image_key in ['image_ref', 'kernel_id', 'ramdisk_id']:
try:
image_ref_str = str(instance[image_key])
except KeyError:
continue
local, remote, insts = used_images.get(image_ref_str,
(0, 0, []))
if instance['host'] == CONF.host:
local += 1
else:
remote += 1
insts.append(instance['name'])
used_images[image_ref_str] = (local, remote, insts)
image_popularity.setdefault(image_ref_str, 0)
image_popularity[image_ref_str] += 1
return {'used_images': used_images,
'image_popularity': image_popularity,
'instance_names': instance_names}
def _list_base_images(self, base_dir):
"""Return a list of the images present in _base.
Note that this does not return a value. It instead populates a class
variable with a list of images that we need to try and explain.
This method returns a dictionary with the following keys:
- unexplained_images
- originals
"""
return {'unexplained_images': [],
'originals': []}
def _age_and_verify_cached_images(self, context, all_instances, base_dir):
"""Ages and verfies cached images."""
raise NotImplementedError()
def update(self, context, all_instances):
"""The cache manager.
This will invoke the cache manager. This will update the cache
according to the defined cache management scheme. The information
populated in the cached stats will be used for the cache management.
"""
raise NotImplementedError()

View File

@ -4605,7 +4605,7 @@ class LibvirtDriver(driver.ComputeDriver):
def manage_image_cache(self, context, all_instances):
"""Manage the local cache of images."""
self.image_cache_manager.verify_base_images(context, all_instances)
self.image_cache_manager.update(context, all_instances)
def _cleanup_remote_migration(self, dest, inst_base, inst_base_resize,
shared_storage=False):

View File

@ -83,7 +83,7 @@ __imagebackend_opts = [
CONF = cfg.CONF
CONF.register_opts(__imagebackend_opts, 'libvirt')
CONF.import_opt('image_cache_subdirectory_name', 'nova.compute.manager')
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
CONF.import_opt('preallocate_images', 'nova.virt.driver')
LOG = logging.getLogger(__name__)

View File

@ -30,13 +30,12 @@ import time
from oslo.config import cfg
from nova.compute import task_states
from nova.compute import vm_states
from nova.openstack.common import fileutils
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt import imagecache
from nova.virt.libvirt import utils as virtutils
LOG = logging.getLogger(__name__)
@ -48,10 +47,6 @@ imagecache_opts = [
help='Allows image information files to be stored in '
'non-standard locations',
deprecated_group='DEFAULT'),
cfg.BoolOpt('remove_unused_base_images',
default=True,
help='Should unused base images be removed?',
deprecated_group='DEFAULT'),
cfg.BoolOpt('remove_unused_kernels',
default=False,
help='Should unused kernel images be removed? This is only '
@ -64,11 +59,6 @@ imagecache_opts = [
help='Unused resized base images younger than this will not be '
'removed',
deprecated_group='DEFAULT'),
cfg.IntOpt('remove_unused_original_minimum_age_seconds',
default=(24 * 3600),
help='Unused unresized base images younger than this will not '
'be removed',
deprecated_group='DEFAULT'),
cfg.BoolOpt('checksum_base_images',
default=False,
help='Write a checksum for files in _base to disk',
@ -81,9 +71,8 @@ imagecache_opts = [
CONF = cfg.CONF
CONF.register_opts(imagecache_opts, 'libvirt')
CONF.import_opt('host', 'nova.netconf')
CONF.import_opt('instances_path', 'nova.compute.manager')
CONF.import_opt('image_cache_subdirectory_name', 'nova.compute.manager')
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
def get_cache_fname(images, key):
@ -248,8 +237,9 @@ def write_stored_checksum(target):
write_stored_info(target, field='sha1', value=_hash_file(target))
class ImageCacheManager(object):
class ImageCacheManager(imagecache.ImageCacheManager):
def __init__(self):
super(ImageCacheManager, self).__init__()
self.lock_path = os.path.join(CONF.instances_path, 'locks')
self._reset_state()
@ -284,6 +274,7 @@ class ImageCacheManager(object):
Note that this does not return a value. It instead populates a class
variable with a list of images that we need to try and explain.
"""
digest_size = hashlib.sha1().digestsize * 2
for ent in os.listdir(base_dir):
if len(ent) == digest_size:
@ -294,44 +285,8 @@ class ImageCacheManager(object):
not is_valid_info_file(os.path.join(base_dir, ent))):
self._store_image(base_dir, ent, original=False)
def _list_running_instances(self, context, all_instances):
"""List running instances (on all compute nodes)."""
self.used_images = {}
self.image_popularity = {}
self.instance_names = set()
for instance in all_instances:
# NOTE(mikal): "instance name" here means "the name of a directory
# which might contain an instance" and therefore needs to include
# historical permutations as well as the current one.
self.instance_names.add(instance['name'])
self.instance_names.add(instance['uuid'])
resize_states = [task_states.RESIZE_PREP,
task_states.RESIZE_MIGRATING,
task_states.RESIZE_MIGRATED,
task_states.RESIZE_FINISH]
if instance['task_state'] in resize_states or \
instance['vm_state'] == vm_states.RESIZED:
self.instance_names.add(instance['name'] + '_resize')
self.instance_names.add(instance['uuid'] + '_resize')
for image_key in ['image_ref', 'kernel_id', 'ramdisk_id']:
try:
image_ref_str = str(instance[image_key])
except KeyError:
continue
local, remote, insts = self.used_images.get(image_ref_str,
(0, 0, []))
if instance['host'] == CONF.host:
local += 1
else:
remote += 1
insts.append(instance['name'])
self.used_images[image_ref_str] = (local, remote, insts)
self.image_popularity.setdefault(image_ref_str, 0)
self.image_popularity[image_ref_str] += 1
return {'unexplained_images': self.unexplained_images,
'originals': self.originals}
def _list_backing_images(self):
"""List the backing images currently in use."""
@ -364,7 +319,6 @@ class ImageCacheManager(object):
{'instance': ent,
'backing': backing_file})
self.unexplained_images.remove(backing_path)
return inuse_images
def _find_base_file(self, base_dir, fingerprint):
@ -474,7 +428,7 @@ class ImageCacheManager(object):
maxage = CONF.libvirt.remove_unused_resized_minimum_age_seconds
if base_file in self.originals:
maxage = CONF.libvirt.remove_unused_original_minimum_age_seconds
maxage = CONF.remove_unused_original_minimum_age_seconds
if age < maxage:
LOG.info(_('Base file too young to remove: %s'),
@ -561,31 +515,8 @@ class ImageCacheManager(object):
virtutils.chown(base_file, os.getuid())
os.utime(base_file, None)
def verify_base_images(self, context, all_instances):
"""Verify that base images are in a reasonable state."""
# NOTE(mikal): The new scheme for base images is as follows -- an
# image is streamed from the image service to _base (filename is the
# sha1 hash of the image id). If CoW is enabled, that file is then
# resized to be the correct size for the instance (filename is the
# same as the original, but with an underscore and the resized size
# in bytes). This second file is then CoW'd to the instance disk. If
# CoW is disabled, the resize occurs as part of the copy from the
# cache to the instance directory. Files ending in _sm are no longer
# created, but may remain from previous versions.
self._reset_state()
base_dir = os.path.join(CONF.instances_path,
CONF.image_cache_subdirectory_name)
if not os.path.exists(base_dir):
LOG.debug(_('Skipping verification, no base directory at %s'),
base_dir)
return
def _age_and_verify_cached_images(self, context, all_instances, base_dir):
LOG.debug(_('Verify base images'))
self._list_base_images(base_dir)
self._list_running_instances(context, all_instances)
# Determine what images are on disk because they're in use
for img in self.used_images:
fingerprint = hashlib.sha1(img).hexdigest()
@ -622,9 +553,45 @@ class ImageCacheManager(object):
LOG.info(_('Removable base files: %s'),
' '.join(self.removable_base_files))
if CONF.libvirt.remove_unused_base_images:
if self.remove_unused_base_images:
for base_file in self.removable_base_files:
self._remove_base_file(base_file)
# That's it
LOG.debug(_('Verification complete'))
def _get_base(self):
# NOTE(mikal): The new scheme for base images is as follows -- an
# image is streamed from the image service to _base (filename is the
# sha1 hash of the image id). If CoW is enabled, that file is then
# resized to be the correct size for the instance (filename is the
# same as the original, but with an underscore and the resized size
# in bytes). This second file is then CoW'd to the instance disk. If
# CoW is disabled, the resize occurs as part of the copy from the
# cache to the instance directory. Files ending in _sm are no longer
# created, but may remain from previous versions.
base_dir = os.path.join(CONF.instances_path,
CONF.image_cache_subdirectory_name)
if not os.path.exists(base_dir):
LOG.debug(_('Skipping verification, no base directory at %s'),
base_dir)
return
return base_dir
def update(self, context, all_instances):
base_dir = self._get_base()
if not base_dir:
return
# reset the local statistics
self._reset_state()
# read the cached images
self._list_base_images(base_dir)
# read running instances data
running = self._list_running_instances(context, all_instances)
self.used_images = running['used_images']
self.image_popularity = running['image_popularity']
self.instance_names = running['instance_names']
# perform the aging and image verification
self._age_and_verify_cached_images(context, all_instances, base_dir)

View File

@ -65,7 +65,7 @@ vmware_group = cfg.OptGroup(name='vmware',
CONF = cfg.CONF
CONF.register_group(vmware_group)
CONF.register_opts(vmware_vif_opts, vmware_group)
CONF.import_opt('image_cache_subdirectory_name', 'nova.compute.manager')
CONF.import_opt('image_cache_subdirectory_name', 'nova.virt.imagecache')
CONF.import_opt('vnc_enabled', 'nova.vnc')
LOG = logging.getLogger(__name__)