Exemple #1
0
    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)
Exemple #2
0
 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
Exemple #3
0
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)
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
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)