Example #1
0
    def add(self, image_id, image_file, image_size):
        """
        Stores an image file with supplied identifier to the backend
        storage system and returns a tuple containing information
        about the stored image.

        :param image_id: The opaque image identifier
        :param image_file: The image data to write, as a file-like object
        :param image_size: The size of the image data to write, in bytes

        :retval tuple of URL in backing store, bytes written, checksum
                and a dictionary with storage system specific information
        :raises `glance.common.exception.Duplicate` if the image already
                existed

        :note By default, the backend writes the image data to a file
              `/<DATADIR>/<ID>`, where <DATADIR> is the value of
              the filesystem_store_datadir configuration option and <ID>
              is the supplied image ID.
        """
        datadir = self._find_best_datadir(image_size)
        filepath = os.path.join(datadir, str(image_id))

        if os.path.exists(filepath):
            raise exception.Duplicate(
                _("Image file %s already exists!") % filepath)

        checksum = hashlib.md5()
        bytes_written = 0
        try:
            with open(filepath, 'wb') as f:
                for buf in utils.chunkreadable(image_file,
                                               ChunkedFile.CHUNKSIZE):
                    bytes_written += len(buf)
                    checksum.update(buf)
                    f.write(buf)
        except IOError as e:
            if e.errno != errno.EACCES:
                self._delete_partial(filepath, image_id)
            exceptions = {
                errno.EFBIG: exception.StorageFull(),
                errno.ENOSPC: exception.StorageFull(),
                errno.EACCES: exception.StorageWriteDenied()
            }
            raise exceptions.get(e.errno, e)
        except Exception:
            with excutils.save_and_reraise_exception():
                self._delete_partial(filepath, image_id)

        checksum_hex = checksum.hexdigest()
        metadata = self._get_metadata()

        LOG.debug(
            _("Wrote %(bytes_written)d bytes to %(filepath)s with "
              "checksum %(checksum_hex)s"), {
                  'bytes_written': bytes_written,
                  'filepath': filepath,
                  'checksum_hex': checksum_hex
              })
        return ('file://%s' % filepath, bytes_written, checksum_hex, metadata)
Example #2
0
 def add_to_backend(self,
                    conf,
                    image_id,
                    data,
                    size,
                    scheme=None,
                    context=None,
                    verifier=None):
     store_max_size = 7
     current_store_size = 2
     for location in self.data.keys():
         if image_id in location:
             raise exception.Duplicate()
     if not size:
         # 'data' is a string wrapped in a LimitingReader|CooperativeReader
         # pipeline, so peek under the hood of those objects to get at the
         # string itself.
         size = len(data.data.fd)
     if (current_store_size + size) > store_max_size:
         raise exception.StorageFull()
     if context.user == USER2:
         raise exception.Forbidden()
     if context.user == USER3:
         raise exception.StorageWriteDenied()
     self.data[image_id] = (data, size)
     checksum = 'Z'
     return (image_id, size, checksum, self.store_metadata)
Example #3
0
    def add(self, image_id, image_file, image_size):
        """
        Stores an image file with supplied identifier to the backend
        storage system and returns an `glance.store.ImageAddResult` object
        containing information about the stored image.

        :param image_id: The opaque image identifier
        :param image_file: The image data to write, as a file-like object
        :param image_size: The size of the image data to write, in bytes

        :retval `glance.store.ImageAddResult` object
        :raises `glance.common.exception.Duplicate` if the image already
                existed

        :note By default, the backend writes the image data to a file
              `/<DATADIR>/<ID>`, where <DATADIR> is the value of
              the filesystem_store_datadir configuration option and <ID>
              is the supplied image ID.
        """

        filepath = os.path.join(self.datadir, str(image_id))

        if os.path.exists(filepath):
            raise exception.Duplicate(
                _("Image file %s already exists!") % filepath)

        checksum = hashlib.md5()
        bytes_written = 0
        try:
            with open(filepath, 'wb') as f:
                for buf in utils.chunkreadable(image_file,
                                               ChunkedFile.CHUNKSIZE):
                    bytes_written += len(buf)
                    checksum.update(buf)
                    f.write(buf)
        except IOError as e:
            if e.errno in [errno.EFBIG, errno.ENOSPC]:
                try:
                    os.unlink(filepath)
                except Exception:
                    msg = _('Unable to remove partial image data for image %s')
                    LOG.error(msg % image_id)
                raise exception.StorageFull()
            elif e.errno == errno.EACCES:
                raise exception.StorageWriteDenied()
            else:
                raise

        checksum_hex = checksum.hexdigest()

        LOG.debug(
            _("Wrote %(bytes_written)d bytes to %(filepath)s with "
              "checksum %(checksum_hex)s") % locals())
        return ('file://%s' % filepath, bytes_written, checksum_hex)
Example #4
0
 def add_to_backend(self, context, scheme, image_id, data, size):
     store_max_size = 7
     current_store_size = 2
     for location in self.data.keys():
         if image_id in location:
             raise exception.Duplicate()
     if size and (current_store_size + size) > store_max_size:
         raise exception.StorageFull()
     if context.user == USER2:
         raise exception.Forbidden()
     if context.user == USER3:
         raise exception.StorageWriteDenied()
     self.data[image_id] = (data, size or len(data))
     checksum = 'Z'
     return (image_id, size, checksum)
Example #5
0
    def add(self, image_id, image_file, image_size):
        """
        Stores an image file with supplied identifier to the backend
        storage system and returns an `glance.store.ImageAddResult` object
        containing information about the stored image.

        :param image_id: The opaque image identifier
        :param image_file: The image data to write, as a file-like object
        :param image_size: The size of the image data to write, in bytes

        :retval `glance.store.ImageAddResult` object
        :raises `glance.common.exception.Duplicate` if the image already
                existed

        :note By default, the backend writes the image data to a file
              `/<DATADIR>/<ID>`, where <DATADIR> is the value of
              the filesystem_store_datadir configuration option and <ID>
              is the supplied image ID.
        """

        filepath = os.path.join(self.datadir, str(image_id))

        if os.path.exists(filepath):
            raise exception.Duplicate(
                _("Image file %s already exists!") % filepath)

        checksum = hashlib.md5()
        bytes_written = 0
        try:
            with open(filepath, 'wb') as f:
                while True:
                    buf = image_file.read(ChunkedFile.CHUNKSIZE)
                    if not buf:
                        break
                    bytes_written += len(buf)
                    checksum.update(buf)
                    f.write(buf)
        except IOError:
            raise exception.StorageFull()
        checksum_hex = checksum.hexdigest()

        logger.debug(
            _("Wrote %(bytes_written)d bytes to %(filepath)s with "
              "checksum %(checksum_hex)s") % locals())
        return ('file://%s' % filepath, bytes_written, checksum_hex)
Example #6
0
    def _find_best_datadir(self, image_size):
        """Finds the best datadir by priority and free space.

        Traverse directories returning the first one that has sufficient
        free space, in priority order. If two suitable directories have
        the same priority, choose the one with the most free space
        available.
        :image_size size of image being uploaded.
        :returns best_datadir as directory path of the best priority datadir.
        :raises exception.StorageFull if there is no datadir in
                self.priority_data_map that can accommodate the image.
        """
        if not self.multiple_datadirs:
            return self.datadir

        best_datadir = None
        max_free_space = 0
        for priority in self.priority_list:
            for datadir in self.priority_data_map.get(priority):
                free_space = self._get_capacity_info(datadir)
                if free_space >= image_size and free_space > max_free_space:
                    max_free_space = free_space
                    best_datadir = datadir

            # If datadir is found which can accommodate image and has maximum
            # free space for the given priority then break the loop,
            # else continue to lookup further.
            if best_datadir:
                break
        else:
            msg = (_("There is no enough disk space left on the image "
                     "storage media. requested=%s") % image_size)
            LOG.exception(msg)
            raise exception.StorageFull(message=msg)

        return best_datadir
Example #7
0
 def data_iterator():
     self.notifier.log = []
     yield 'abcde'
     raise exception.StorageFull('Modern Major General')