def verify_base_size(self, 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 = self.get_disk_size(base) if size < base_size: msg = _LE('%(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.FlavorDiskSmallerThanImage(flavor_size=size, image_size=base_size)
def fetch_to_raw(context, image_href, path, max_size=0): path_tmp = "%s.part" % path fetch(context, image_href, path_tmp, 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: LOG.error(_LE('%(base)s virtual size %(disk_size)s ' 'larger than flavor root disk size %(size)s'), {'base': path, 'disk_size': disk_size, 'size': max_size}) raise exception.FlavorDiskSmallerThanImage( flavor_size=max_size, image_size=disk_size) 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): try: convert_image(path_tmp, staged, fmt, 'raw') except exception.ImageUnacceptable as exp: # re-raise to include image_href raise exception.ImageUnacceptable(image_id=image_href, reason=_("Unable to convert image to raw: %(exp)s") % {'exp': exp}) 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)
def _is_resize_needed(self, vhd_path, old_size, new_size, instance): if new_size < old_size: raise exception.FlavorDiskSmallerThanImage( flavor_size=new_size, image_size=old_size) elif new_size > old_size: LOG.debug("Resizing VHD %(vhd_path)s to new " "size %(new_size)s" % {'new_size': new_size, 'vhd_path': vhd_path}, instance=instance) return True return False
def _resize_and_cache_vhd(self, instance, vhd_path): vhd_size = self._vhdutils.get_vhd_size(vhd_path)['VirtualSize'] root_vhd_size_gb = self._get_root_vhd_size_gb(instance) root_vhd_size = root_vhd_size_gb * units.Gi root_vhd_internal_size = ( self._vhdutils.get_internal_vhd_size_by_file_size( vhd_path, root_vhd_size)) if root_vhd_internal_size < vhd_size: raise exception.FlavorDiskSmallerThanImage( flavor_size=root_vhd_size, image_size=vhd_size) if root_vhd_internal_size > vhd_size: path_parts = os.path.splitext(vhd_path) resized_vhd_path = '%s_%s%s' % (path_parts[0], root_vhd_size_gb, path_parts[1]) lock_path = os.path.dirname(resized_vhd_path) lock_name = "%s-cache.lock" % os.path.basename(resized_vhd_path) @utils.synchronized(name=lock_name, external=True, lock_path=lock_path) def copy_and_resize_vhd(): if not self._pathutils.exists(resized_vhd_path): try: LOG.debug( "Copying VHD %(vhd_path)s to " "%(resized_vhd_path)s", { 'vhd_path': vhd_path, 'resized_vhd_path': resized_vhd_path }) self._pathutils.copyfile(vhd_path, resized_vhd_path) LOG.debug( "Resizing VHD %(resized_vhd_path)s to new " "size %(root_vhd_size)s", { 'resized_vhd_path': resized_vhd_path, 'root_vhd_size': root_vhd_size }) self._vhdutils.resize_vhd(resized_vhd_path, root_vhd_internal_size, is_file_max_size=False) except Exception: with excutils.save_and_reraise_exception(): if self._pathutils.exists(resized_vhd_path): self._pathutils.remove(resized_vhd_path) copy_and_resize_vhd() return resized_vhd_path
def _verify_rescue_image(self, instance, rescue_image_id, rescue_image_path): rescue_image_info = self._vhdutils.get_vhd_info(rescue_image_path) rescue_image_size = rescue_image_info['VirtualSize'] flavor_disk_size = instance.root_gb * units.Gi if rescue_image_size > flavor_disk_size: err_msg = _('Using a rescue image bigger than the instance ' 'flavor disk size is not allowed. ' 'Rescue image size: %(rescue_image_size)s. ' 'Flavor disk size:%(flavor_disk_size)s. ' 'Rescue image id %(rescue_image_id)s.') raise exception.FlavorDiskSmallerThanImage(err_msg % {'rescue_image_size': rescue_image_size, 'flavor_disk_size': flavor_disk_size, 'rescue_image_id': rescue_image_id})