From c867e089b1a945bd4181cbe651ad12649b345674 Mon Sep 17 00:00:00 2001 From: Michael Still Date: Thu, 16 Feb 2012 08:00:09 +1100 Subject: Rework base file checksums. The libvirt image cache manager wants to verify that the images it is managing have not become corrupt. We therefore write checksums for these images to disk and verify them as part of the cache management periodic task. This checksumming was originally done as part of the setup for a new virtual machine. This has been refactored so that generating a checksum on a large file will not delay the startup of a VM. Note that these checksums are intended for detecting corruption, not malicious system admins. Change-Id: I781877b342207cb2cf03eb48bd89456846cbf487 --- nova/virt/libvirt/connection.py | 5 ++++ nova/virt/libvirt/imagecache.py | 53 +++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 13 deletions(-) (limited to 'nova/virt') diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index e8fdcd792..634acdeca 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -921,6 +921,11 @@ class LibvirtConnection(driver.ComputeDriver): If size is specified, we attempt to resize up to that size. """ + # NOTE(mikal): Checksums aren't created here, even if the image cache + # manager is enabled, as that would slow down VM startup. If both + # cache management and checksumming are enabled, then the checksum + # will be created on the first pass of the image cache manager. + generating = 'image_id' not in kwargs if not os.path.exists(target): base_dir = os.path.join(FLAGS.instances_path, '_base') diff --git a/nova/virt/libvirt/imagecache.py b/nova/virt/libvirt/imagecache.py index bd714ea7f..6226e8d4e 100644 --- a/nova/virt/libvirt/imagecache.py +++ b/nova/virt/libvirt/imagecache.py @@ -55,6 +55,9 @@ imagecache_opts = [ default=(24 * 3600), help='Unused unresized base images younger than this will not ' 'be removed'), + cfg.BoolOpt('checksum_base_images', + default=False, + help='Write a checksum for files in _base to disk'), ] flags.DECLARE('instances_path', 'nova.compute.manager') @@ -77,6 +80,23 @@ def read_stored_checksum(base_file): return stored_checksum +def write_stored_checksum(target): + """Write a checksum to disk for a file in _base.""" + + checksum_filename = '%s.sha1' % target + if os.path.exists(target) and not os.path.exists(checksum_filename): + # NOTE(mikal): Create the checksum file first to exclude possible + # overlap in checksum operations. An empty checksum file is ignored if + # encountered during verification. + sum_file = open(checksum_filename, 'w') + img_file = open(target, 'r') + checksum = utils.hash_file(img_file) + img_file.close() + + sum_file.write(checksum) + sum_file.close() + + class ImageCacheManager(object): def __init__(self): self.unexplained_images = [] @@ -200,13 +220,13 @@ class ImageCacheManager(object): action occurs. This is something sysadmins should monitor for and handle manually when it occurs. """ - f = open(base_file, 'r') - current_checksum = utils.hash_file(f) - f.close() stored_checksum = read_stored_checksum(base_file) - if stored_checksum: + f = open(base_file, 'r') + current_checksum = utils.hash_file(f) + f.close() + if current_checksum != stored_checksum: LOG.error(_('%(container_format)s-%(id)s ' '(%(base_file)s): ' @@ -225,6 +245,13 @@ class ImageCacheManager(object): {'container_format': img['container_format'], 'id': img['id'], 'base_file': base_file}) + + # NOTE(mikal): If the checksum file is missing, then we should + # create one. We don't create checksums when we download images + # from glance because that would delay VM startup. + if FLAGS.checksum_base_images: + write_stored_checksum(base_file) + return None def _remove_base_file(self, base_file): @@ -335,15 +362,15 @@ class ImageCacheManager(object): # TODO(mikal): Write a unit test for this method # TODO(mikal): Handle "generated" images - # 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. + # 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.unexplained_images = [] self.active_base_files = [] -- cgit