예제 #1
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskWriter object to encapsulate the state.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        """
        if not exists(self.tmpdir):
            mkdirs(self.tmpdir)
        fd, tmppath = mkstemp(dir=self.tmpdir)
        try:
            if size is not None and size > 0:
                try:
                    fallocate(fd, size)
                except OSError:
                    raise DiskFileNoSpace()
            yield DiskWriter(self, fd, tmppath, self.threadpool)
        finally:
            try:
                os.close(fd)
            except OSError:
                pass
            try:
                os.unlink(tmppath)
            except OSError:
                pass
예제 #2
0
    def _write_metadata(self, path, metadata, fd=None):
        meta_dir_path, meta_file_path = self._get_metadata_dir(path)

        # create metadata dir if it doesn't exist
        # TODO error handling
        if not os.path.exists(meta_dir_path):
            mkdirs(meta_dir_path)

        metastr = serialize_metadata(metadata)

        tmpfile = meta_file_path + '_' + uuid4().hex
        try:
            with open(tmpfile, 'wt') as f:
                f.write(metastr)
        except IOError as err:
            if err.errno in (errno.ENOSPC, errno.EDQUOT):
                do_log_rl("_write_metadata failed: %s : %s",
                          err, meta_file_path)
                raise DiskFileNoSpace()
            else:
                raise FileConnectorFileSystemIOError(
                    err.errno,
                    '_write_metadata failed: %s : %s' %
                    (err, meta_file_path))

        try:
            do_rename(tmpfile, meta_file_path)
        except OSError as err:
            # TODO better error handling
            raise FileConnectorFileSystemOSError(
                err.errno, "%s, rename('%s', '%s')" % (
                    err.strerror, tmpfile, meta_file_path))
예제 #3
0
    def write_metadata(self, fd, metadata, obj_path, xattr_size=65536):
        """
        Helper function to write pickled metadata for an object file.

        :param obj_path: full path of the file
        :param metadata: metadata to write
        """
        metastr = pickle.dumps(metadata, PICKLE_PROTOCOL)
        key = 0
        while metastr:
            try:
                xattr.setxattr(fd, '%s%s' % (SWIFT_METADATA_KEY, key or ''),
                               metastr[:xattr_size])
                metastr = metastr[xattr_size:]
                key += 1
            except IOError as e:
                for err in 'ENOTSUP', 'EOPNOTSUPP':
                    if hasattr(errno, err) and e.errno == getattr(errno, err):
                        msg = "Filesystem at %s does not support xattr" % \
                              obj_path
                        logging.exception(msg)
                        raise DiskFileXattrNotSupported(e)
                if e.errno in (errno.ENOSPC, errno.EDQUOT):
                    msg = "No space left on device for %s" % obj_path
                    logging.exception(msg)
                    raise DiskFileNoSpace()
                raise
 def write(self, obj, offset, data):
     try:
         return self._ioctx.write(obj, data, offset)
     except self._fs.RADOS.NoSpace:
         raise DiskFileNoSpace()
     except Exception:
         raise DiskFileError()
예제 #5
0
def write_metadata(path_or_fd, metadata):
    """
    Helper function to write pickled metadata for a File/Directory.

    :param path_or_fd: File/Directory path or fd to write the metadata
    :param metadata: dictionary of metadata write
    """
    assert isinstance(metadata, dict)
    metastr = pickle.dumps(metadata, PICKLE_PROTOCOL)
    key = 0
    while metastr:
        try:
            do_setxattr(path_or_fd, '%s%s' % (METADATA_KEY, key or ''),
                        metastr[:MAX_XATTR_SIZE])
        except IOError as err:
            if err.errno in (errno.ENOSPC, errno.EDQUOT):
                if isinstance(path_or_fd, int):
                    filename = get_filename_from_fd(path_or_fd)
                    do_log_rl("write_metadata(%d, metadata) failed: %s : %s",
                              path_or_fd, err, filename)
                else:
                    do_log_rl("write_metadata(%s, metadata) failed: %s",
                              path_or_fd, err)
                raise DiskFileNoSpace()
            else:
                raise GlusterFileSystemIOError(
                    err.errno,
                    'setxattr("%s", %s, metastr)' % (path_or_fd, key))
        metastr = metastr[MAX_XATTR_SIZE:]
        key += 1
예제 #6
0
def write_metadata(fd, metadata, xattr_size=65536, md_key=None):
    """
    Helper function to write pickled metadata for an object file.

    :param fd: file descriptor or filename to write the metadata
    :param md_key: metadata key to be write to object file
    :param metadata: metadata to write
    """
    meta_key = SWIFT_METADATA_KEY

    metastr = pickle.dumps(metadata, PICKLE_PROTOCOL)
    key = 0
    while metastr:
        try:
            xattr.setxattr(fd, '%s%s' % (meta_key, key or ''),
                           metastr[:xattr_size])
            metastr = metastr[xattr_size:]
            key += 1
        except IOError as e:
            for err in 'ENOTSUP', 'EOPNOTSUPP':
                if hasattr(errno, err) and e.errno == getattr(errno, err):
                    msg = "Filesystem at %s does not support xattr" % \
                          get_filename(fd)
                    logging.exception(msg)
                    raise DiskFileXattrNotSupported(e)
            if e.errno in (errno.ENOSPC, errno.EDQUOT):
                msg = "No space left on device for %s" % get_filename(fd)
                logging.exception(msg)
                raise DiskFileNoSpace()
            raise
예제 #7
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        """
        if not exists(self._tmpdir):
            mkdirs(self._tmpdir)
        fd, tmppath = mkstemp(dir=self._tmpdir)
        try:
            if size is not None and size > 0:
                try:
                    fallocate(fd, size)
                except OSError:
                    raise DiskFileNoSpace()
            yield DiskFileWriter(self._name, self._datadir, fd, tmppath,
                                 self._bytes_per_sync, self._threadpool)
        finally:
            try:
                os.close(fd)
            except OSError:
                pass
            try:
                os.unlink(tmppath)
            except OSError:
                pass
예제 #8
0
def do_close(fd):
    try:
        os.close(fd)
    except OSError as err:
        if err.errno in (errno.ENOSPC, errno.EDQUOT):
            filename = get_filename_from_fd(fd)
            do_log_rl("do_close(%d) failed: %s : %s", fd, err, filename)
            raise DiskFileNoSpace()
        else:
            raise SwiftOnFileSystemOSError(
                err.errno, '%s, os.close(%s)' % (err.strerror, fd))
예제 #9
0
def do_write(fd, buf):
    try:
        cnt = os.write(fd, buf)
    except OSError as err:
        filename = get_filename_from_fd(fd)
        if err.errno in (errno.ENOSPC, errno.EDQUOT):
            do_log_rl("do_write(%d, msg[%d]) failed: %s : %s",
                      fd, len(buf), err, filename)
            raise DiskFileNoSpace()
        else:
            raise FileConnectorFileSystemOSError(
                err.errno, '%s, os.write("%s", ...)' % (err.strerror, fd))
    return cnt
예제 #10
0
def mkdirs(path):
    """
    Ensures the path is a directory or makes it if not. Errors if the path
    exists but is a file or on permissions failure.

    :param path: path to create
    """
    try:
        os.makedirs(path)
    except OSError as err:
        if err.errno == errno.EEXIST and os.path.isdir(path):
            return
        elif err.errno in (errno.ENOSPC, errno.EDQUOT):
            do_log_rl("mkdirs(%s) failed: %s", path, err)
            raise DiskFileNoSpace()
        else:
            raise FileConnectorFileSystemOSError(
                err.errno, '%s, os.makedirs("%s")' % (err.strerror, path))
예제 #11
0
    def _write_metadata(self, path, metadata, fd=None):
        """
        Helper function to write serialized metadata for a File/Directory.

        :param path: File/Directory path to write the metadata
        :param metadata: dictionary of metadata write
        :param fd: file descriptor to write the metadata, when fd is passed in
                   it will be used instead of path
        """
        if fd:
            path = fd

        assert isinstance(metadata, dict)
        metastr = serialize_metadata(metadata)
        key = 0
        while metastr:
            try:
                do_setxattr(path,
                            '%s%s' % (METADATA_KEY, key or ''),
                            metastr[:MAX_XATTR_SIZE])
            except IOError as err:
                if err.errno in (errno.ENOSPC, errno.EDQUOT):
                    if isinstance(path, int):
                        filename = get_filename_from_fd(path)
                        do_log_rl("write_metadata(%d, md) failed: %s : %s",
                                  path, err, filename)
                    else:
                        do_log_rl("write_metadata(%s, md) failed: %s",
                                  path, err)
                    raise DiskFileNoSpace()
                else:
                    raise FileConnectorFileSystemIOError(
                        err.errno,
                        'setxattr("%s", %s, metastr)' % (path, key))
            metastr = metastr[MAX_XATTR_SIZE:]
            key += 1
예제 #12
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """
        # Create /account/container directory structure on mount point root
        try:
            os.makedirs(self._container_path)
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise

        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        attempts = 1
        while True:
            # To know more about why following temp file naming convention is
            # used, please read this GlusterFS doc:
            # https://github.com/gluster/glusterfs/blob/master/doc/features/dht.md#rename-optimizations  # noqa
            tmpfile = '.' + self._obj + '.' + uuid4().hex
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except SwiftOnFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory' %
                                              (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError(
                        'DiskFile.mkstemp(): failed to'
                        ' successfully create a temporary file'
                        ' without running into a name conflict'
                        ' after %d of %d attempts for: %s' %
                        (attempts, MAX_OPEN_ATTEMPTS, data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # FIXME: Possible FUSE issue or race condition, let's
                    # sleep on it and retry the operation.
                    _random_sleep()
                    logging.warn(
                        "DiskFile.mkstemp(): %s ... retrying in"
                        " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # could be a FUSE issue or some race condition, so let's
                    # sleep a bit and retry.
                    _random_sleep()
                    logging.warn(
                        "DiskFile.mkstemp(): %s ... retrying in"
                        " 0.1 secs", gerr)
                    attempts += 1
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.mkstemp(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            do_fchown(fd, self._uid, self._gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely since at the time of this writing FUSE does not
            # support it.
            dw = DiskFileWriter(fd, tmppath, self)
            yield dw
        finally:
            dw.close()
            if dw._tmppath:
                do_unlink(dw._tmppath)
 def create(self, obj, size):
     try:
         self._ioctx.trunc(obj, size)
     except self._fs.RADOS.NoSpace:
         raise DiskFileNoSpace()
예제 #14
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """
        # Create /account/container directory structure on mount point root
        try:
            os.makedirs(self._container_path)
        except OSError as err:
            if err.errno != errno.EEXIST:
                raise

        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        fd = None
        attempts = 1
        while True:
            # To know more about why following temp file naming convention is
            # used, please read this GlusterFS doc:
            # https://github.com/gluster/glusterfs/blob/master/doc/features/dht.md#rename-optimizations  # noqa
            tmpfile = '.' + self._obj + '.' + uuid4().hex
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except SwiftOnFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory' %
                                              (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError(
                        'DiskFile.create(): failed to'
                        ' successfully create a temporary file'
                        ' without running into a name conflict'
                        ' after %d of %d attempts for: %s' %
                        (attempts, MAX_OPEN_ATTEMPTS, data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # FIXME: Possible FUSE issue or race condition, let's
                    # sleep on it and retry the operation.
                    _random_sleep()
                    logging.warn(
                        "DiskFile.create(): %s ... retrying in"
                        " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # ENOENT
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # could be a FUSE issue or some race condition, so let's
                    # sleep a bit and retry.
                    # Handle race:
                    # This can be the issue when memcache has cached that the
                    # container exists. If someone removes the container dir
                    # from filesystem, it's not reflected in memcache. So
                    # swift reports that the container exists and this code
                    # tries to create a file in a directory that does not
                    # exist. However, it's wrong to create the container here.
                    _random_sleep()
                    logging.warn(
                        "DiskFile.create(): %s ... retrying in"
                        " 0.1 secs", gerr)
                    attempts += 1
                    if attempts > 2:
                        # Ideally we would want to return 404 indicating that
                        # the container itself does not exist. Can't be done
                        # though as the caller won't catch DiskFileNotExist.
                        # We raise an exception with a meaningful name for
                        # correctness.
                        logging.warn("Container dir %s does not exist",
                                     self._container_path)
                        raise DiskFileContainerDoesNotExist
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            if size is not None and size > 0:
                try:
                    fallocate(fd, size)
                except OSError as err:
                    if err.errno in (errno.ENOSPC, errno.EDQUOT):
                        raise DiskFileNoSpace()
                    raise
            # Ensure it is properly owned before we make it available.
            if not ((self._uid == DEFAULT_UID) and (self._gid == DEFAULT_GID)):
                # If both UID and GID is -1 (default values), it has no effect.
                # So don't do a fchown.
                # Further, at the time of this writing, UID and GID information
                # is not passed to DiskFile.
                do_fchown(fd, self._uid, self._gid)
            dw = DiskFileWriter(fd, tmppath, self)
            # It's now the responsibility of DiskFileWriter to close this fd.
            fd = None
            yield dw
        finally:
            if dw:
                dw.close()
                if dw._tmppath:
                    do_unlink(dw._tmppath)