def verify_base_size(base, size, base_size=0): """Check that the base image is not larger than size. Since images can't be generally shrunk, enforce this constraint taking account of virtual image size. """ # Note(pbrady): The size and min_disk parameters of a glance # image are checked against the instance size before the image # is even downloaded from glance, but currently min_disk is # adjustable and doesn't currently account for virtual disk size, # so we need this extra check here. # NOTE(cfb): Having a flavor that sets the root size to 0 and having # nova effectively ignore that size and use the size of the # image is considered a feature at this time, not a bug. if size is None: return if size and not base_size: base_size = disk.get_disk_size(base) if size < base_size: msg = _('%(base)s virtual size %(base_size)s ' 'larger than flavor root disk size %(size)s') LOG.error(msg % { 'base': base, 'base_size': base_size, 'size': size }) raise exception.InstanceTypeDiskTooSmall()
def create_image(self, prepare_template, base, size, *args, **kwargs): @utils.synchronized(base, external=True, lock_path=self.lock_path) def copy_qcow2_image(base, target, size): # TODO(pbrady): Consider copying the cow image here # with preallocation=metadata set for performance reasons. # This would be keyed on a 'preallocate_images' setting. libvirt_utils.create_cow_image(base, target) if size: disk.extend(target, size) # Download the unmodified base image unless we already have a copy. if not os.path.exists(base): prepare_template(target=base, *args, **kwargs) legacy_backing_size = None legacy_base = base # Determine whether an existing qcow2 disk uses a legacy backing by # actually looking at the image itself and parsing the output of the # backing file it expects to be using. if os.path.exists(self.path): backing_path = libvirt_utils.get_disk_backing_file(self.path) if backing_path is not None: backing_file = os.path.basename(backing_path) backing_parts = backing_file.rpartition('_') if backing_file != backing_parts[-1] and \ backing_parts[-1].isdigit(): legacy_backing_size = int(backing_parts[-1]) legacy_base += '_%d' % legacy_backing_size legacy_backing_size *= 1024 * 1024 * 1024 # Create the legacy backing file if necessary. if legacy_backing_size: if not os.path.exists(legacy_base): with fileutils.remove_path_on_error(legacy_base): libvirt_utils.copy_image(base, legacy_base) disk.extend(legacy_base, legacy_backing_size) # NOTE(cfb): Having a flavor that sets the root size to 0 and having # nova effectively ignore that size and use the size of the # image is considered a feature at this time, not a bug. disk_size = disk.get_disk_size(base) if size and size < disk_size: msg = _('%(base)s virtual size %(disk_size)s' 'larger than flavor root disk size %(size)s') LOG.error(msg % { 'base': base, 'disk_size': disk_size, 'size': size }) raise exception.InstanceTypeDiskTooSmall() if not os.path.exists(self.path): with fileutils.remove_path_on_error(self.path): copy_qcow2_image(base, self.path, size)
def create_image(self, prepare_template, base, size, *args, **kwargs): @utils.synchronized(base, external=True, lock_path=self.lock_path) def copy_qcow2_image(base, target, size): # TODO(pbrady): Consider copying the cow image here # with preallocation=metadata set for performance reasons. # This would be keyed on a 'preallocate_images' setting. libvirt_utils.create_cow_image(base, target) if size: disk.extend(target, size) if not os.path.exists(base): prepare_template(target=base, *args, **kwargs) # NOTE(cfb): Having a flavor that sets the root size to 0 and having # nova effectively ignore that size and use the size of the # image is considered a feature at this time, not a bug. if size and size < disk.get_disk_size(base): LOG.error('%s virtual size larger than flavor root disk size %s' % (base, size)) raise exception.InstanceTypeDiskTooSmall() if not os.path.exists(self.path): with utils.remove_path_on_error(self.path): copy_qcow2_image(base, self.path, size)
def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0): path_tmp = "%s.part" % path fetch(context, image_href, path_tmp, user_id, project_id, max_size=max_size) with fileutils.remove_path_on_error(path_tmp): data = qemu_img_info(path_tmp) fmt = data.file_format if fmt is None: raise exception.ImageUnacceptable( reason=_("'qemu-img info' parsing failed."), image_id=image_href) backing_file = data.backing_file if backing_file is not None: raise exception.ImageUnacceptable( image_id=image_href, reason=(_("fmt=%(fmt)s backed by: %(backing_file)s") % { 'fmt': fmt, 'backing_file': backing_file })) # We can't generally shrink incoming images, so disallow # images > size of the flavor we're booting. Checking here avoids # an immediate DoS where we convert large qcow images to raw # (which may compress well but not be sparse). # TODO(p-draigbrady): loop through all flavor sizes, so that # we might continue here and not discard the download. # If we did that we'd have to do the higher level size checks # irrespective of whether the base image was prepared or not. disk_size = data.virtual_size if max_size and max_size < disk_size: msg = _('%(base)s virtual size %(disk_size)s ' 'larger than flavor root disk size %(size)s') LOG.error(msg % { 'base': path, 'disk_size': disk_size, 'size': max_size }) raise exception.InstanceTypeDiskTooSmall() if fmt != "raw" and CONF.force_raw_images: staged = "%s.converted" % path LOG.debug("%s was %s, converting to raw" % (image_href, fmt)) with fileutils.remove_path_on_error(staged): convert_image(path_tmp, staged, 'raw') os.unlink(path_tmp) data = qemu_img_info(staged) if data.file_format != "raw": raise exception.ImageUnacceptable( image_id=image_href, reason=_("Converted to raw, but format is now %s") % data.file_format) os.rename(staged, path) else: os.rename(path_tmp, path)