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:
parent
0a2ab98491
commit
6e6b4ad411
|
@ -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
|
||||
|
||||
|
|
|
@ -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__)
|
||||
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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):
|
||||
|
|
|
@ -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__)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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__)
|
||||
|
|
Loading…
Reference in New Issue