def clone(self, context, image_id_or_uri): if not self.driver.supports_layering(): reason = _('installed version of librbd does not support cloning') raise exception.ImageUnacceptable(image_id=image_id_or_uri, reason=reason) image_meta = IMAGE_API.get(context, image_id_or_uri, include_locations=True) locations = image_meta['locations'] LOG.debug('Image locations are: %(locs)s' % {'locs': locations}) if image_meta.get('disk_format') not in ['raw', 'iso']: reason = _('Image is not raw format') raise exception.ImageUnacceptable(image_id=image_id_or_uri, reason=reason) for location in locations: if self.driver.is_cloneable(location, image_meta): return self.driver.clone(location, self.rbd_name) reason = _('No image locations are accessible') raise exception.ImageUnacceptable(image_id=image_id_or_uri, reason=reason)
def parse_url(self, url): prefix = 'rbd://' if not url.startswith(prefix): reason = _('Not stored in rbd') raise exception.ImageUnacceptable(image_id=url, reason=reason) pieces = map(urllib.unquote, url[len(prefix):].split('/')) if '' in pieces: reason = _('Blank components') raise exception.ImageUnacceptable(image_id=url, reason=reason) if len(pieces) != 4: reason = _('Not an rbd snapshot') raise exception.ImageUnacceptable(image_id=url, reason=reason) return pieces
def fetch_image_ova(context, instance, session, vm_name, ds_name, vm_folder_ref, res_pool_ref): """Download the OVA image from the glance image server to the Nova compute node. """ image_ref = instance.image_ref LOG.debug( "Downloading OVA image file %(image_ref)s to the ESX " "as VM named '%(vm_name)s'", { 'image_ref': image_ref, 'vm_name': vm_name }, instance=instance) metadata = IMAGE_API.get(context, image_ref) file_size = int(metadata['size']) vm_import_spec = _build_import_spec_for_import_vapp( session, vm_name, ds_name) read_iter = IMAGE_API.download(context, image_ref) ova_fd, ova_path = tempfile.mkstemp() try: # NOTE(arnaud): Look to eliminate first writing OVA to file system with os.fdopen(ova_fd, 'w') as fp: for chunk in read_iter: fp.write(chunk) with tarfile.open(ova_path, mode="r") as tar: vmdk_name = None for tar_info in tar: if tar_info and tar_info.name.endswith(".ovf"): extracted = tar.extractfile(tar_info.name) xmlstr = extracted.read() vmdk_name = get_vmdk_name_from_ovf(xmlstr) elif vmdk_name and tar_info.name.startswith(vmdk_name): # Actual file name is <vmdk_name>.XXXXXXX extracted = tar.extractfile(tar_info.name) write_handle = rw_handles.VmdkWriteHandle( session, session._host, session._port, res_pool_ref, vm_folder_ref, vm_import_spec, file_size) start_transfer(context, extracted, file_size, write_file_handle=write_handle) extracted.close() LOG.info(_LI("Downloaded OVA image file %(image_ref)s"), {'image_ref': instance.image_ref}, instance=instance) imported_vm_ref = write_handle.get_imported_vm() session._call_method(session.vim, "UnregisterVM", imported_vm_ref) LOG.info(_LI("The imported VM was unregistered"), instance=instance) return raise exception.ImageUnacceptable( reason=_("Extracting vmdk from OVA failed."), image_id=image_ref) finally: os.unlink(ova_path)
def create_image(self, prepare_template, base, size, *args, **kwargs): filename = os.path.split(base)[-1] @utils.synchronized(filename, external=True, lock_path=self.lock_path) def create_ploop_image(base, target, size): image_path = os.path.join(target, "root.hds") libvirt_utils.copy_image(base, image_path) utils.execute('ploop', 'restore-descriptor', '-f', self.pcs_format, target, image_path) if size: dd_path = os.path.join(self.path, "DiskDescriptor.xml") utils.execute('ploop', 'grow', '-s', '%dK' % (size >> 10), dd_path, run_as_root=True) if not os.path.exists(self.path): if CONF.force_raw_images: self.pcs_format = "raw" else: image_meta = IMAGE_API.get(kwargs["context"], kwargs["image_id"]) format = image_meta.get("disk_format") if format == "ploop": self.pcs_format = "expanded" elif format == "raw": self.pcs_format = "raw" else: reason = _("PCS doesn't support images in %s format." " You should either set force_raw_images=True" " in config or upload an image in ploop" " or raw format.") % format raise exception.ImageUnacceptable( image_id=kwargs["image_id"], reason=reason) if not os.path.exists(base): prepare_template(target=base, max_size=size, *args, **kwargs) self.verify_base_size(base, size) if os.path.exists(self.path): return fileutils.ensure_tree(self.path) remove_func = functools.partial(fileutils.delete_if_exists, remove=shutil.rmtree) with fileutils.remove_path_on_error(self.path, remove=remove_func): create_ploop_image(base, self.path, size)
def clone(self, context, image_id_or_uri): """Clone an image. Note that clone operation is backend-dependent. The backend may ask the image API for a list of image "locations" and select one or more of those locations to clone an image from. :param image_id_or_uri: The ID or URI of an image to clone. :raises: exception.ImageUnacceptable if it cannot be cloned """ reason = _('clone() is not implemented') raise exception.ImageUnacceptable(image_id=image_id_or_uri, reason=reason)
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: 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.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') 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)