def _remove_image_on_exec(context, image_href, user_id, project_id, imagehandler_args, image_path): for handler, loc, image_meta in imagehandler.handle_image(context, image_href, user_id, project_id, image_path): # The loop will stop when the handle function returns success. handler.remove_image(context, image_href, image_meta, image_path, user_id, project_id, loc, **imagehandler_args) fileutils.delete_if_exists(image_path)
def _fetch_image(self, image_id, expected_locations, expected_handled_location, expected_handled_path, mock__fetch_image): def _fake_handler_fetch(context, image_id, image_meta, path, user_id=None, project_id=None, location=None): return location == expected_handled_location mock__fetch_image.side_effect = _fake_handler_fetch for handler_context in imagehandler.handle_image(self.context, image_id, target_path=expected_handled_path): (handler, loc, image_meta) = handler_context self.assertEqual(handler, imagehandler._IMAGE_HANDLERS[0]) if (len(expected_locations) > 0): self.assertEqual(expected_locations.pop(0), loc) handler.fetch_image(context, image_id, image_meta, expected_handled_path, location=loc)
def fetch(context, image_href, path, _user_id, _project_id, max_size=0, imagehandler_args=None): """Fetch image and returns whether the image was stored locally.""" imagehandler_args = imagehandler_args or {} _remove_image_fun = functools.partial(_remove_image_on_exec, context, image_href, _user_id, _project_id, imagehandler_args) fetched_to_local = True with fileutils.remove_path_on_error(path, remove=_remove_image_fun): for handler, loc, image_meta in imagehandler.handle_image(context, image_href, _user_id, _project_id, path): # The loop will stop when the handle function returns success. handler.fetch_image(context, image_href, image_meta, path, _user_id, _project_id, loc, **imagehandler_args) fetched_to_local = handler.is_local() return fetched_to_local
def _remove_base_file(self, context, image_id, base_file): """Remove a single base file if it is old enough. Returns nothing. """ if not os.path.exists(base_file): LOG.debug(_('Cannot remove %s, it does not exist'), base_file) return mtime = os.path.getmtime(base_file) age = time.time() - mtime maxage = CONF.libvirt.remove_unused_resized_minimum_age_seconds if base_file in self.originals: maxage = CONF.remove_unused_original_minimum_age_seconds if age < maxage: LOG.info(_('Base file too young to remove: %s'), base_file) else: LOG.info(_('Removing base file: %s'), base_file) try: for handler_context in imagehandler.handle_image( target_path=base_file): (handler, loc, image_meta) = handler_context # The loop will stop when the handle function # returns success. handler.remove_image(context, image_id, image_meta, base_file, location=loc) signature = get_info_filename(base_file) if os.path.exists(signature): os.remove(signature) except OSError as e: LOG.error(_('Failed to remove %(base_file)s, ' 'error was %(error)s'), {'base_file': base_file, 'error': e})
def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0, imagehandler_args=None): path_tmp = "%s.part" % path fetched_to_local = fetch(context, image_href, path_tmp, user_id, project_id, max_size=max_size, imagehandler_args=imagehandler_args) if not fetched_to_local: return imagehandler_args = imagehandler_args or {} _remove_image_fun = functools.partial(_remove_image_on_exec, context, image_href, user_id, project_id, imagehandler_args) with fileutils.remove_path_on_error(path_tmp, remove=_remove_image_fun): 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.FlavorDiskTooSmall() 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') 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) for handler_context in imagehandler.handle_image(context, image_href, user_id, project_id, staged): (handler, loc, image_meta) = handler_context # The loop will stop when the handle function # return success. handler.move_image(context, image_href, image_meta, staged, path, user_id, project_id, loc, **imagehandler_args) for handler_context in imagehandler.handle_image(context, image_href, user_id, project_id, path_tmp): (handler, loc, image_meta) = handler_context handler.remove_image(context, image_href, image_meta, path_tmp, user_id, project_id, loc, **imagehandler_args) else: for handler_context in imagehandler.handle_image(context, image_href, user_id, project_id, path_tmp): (handler, loc, image_meta) = handler_context handler.move_image(context, image_href, image_meta, path_tmp, path, user_id, project_id, loc, **imagehandler_args)