From 027951c1022739a8d53379048349ac2391763d6b Mon Sep 17 00:00:00 2001 From: Chetan Risbud Date: Wed, 31 Jul 2013 19:36:32 +0530 Subject: Gluster to handle ENOSPC (Error 28) correctly A gluster volume could yield an ENOSPC condition seeing that a volume is full. This needed to handled correctly. Added error handling. BUG: 985253 https://bugzilla.redhat.com/show_bug.cgi?id=985253 Change-Id: I85472c0a81a354a2796327fead606da3a938d4bf Signed-off-by: Chetan Risbud Reviewed-on: http://review.gluster.org/5362 Reviewed-by: Peter Portante Reviewed-by: Luis Pabon Tested-by: Luis Pabon --- gluster/swift/common/DiskFile.py | 9 ++++++- gluster/swift/common/exceptions.py | 4 ++++ gluster/swift/obj/server.py | 17 +++++++++++--- test/unit/common/test_diskfile.py | 48 +++++++++++++++++++++++++++++++++++--- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/gluster/swift/common/DiskFile.py b/gluster/swift/common/DiskFile.py index 4b6602e..d64726b 100644 --- a/gluster/swift/common/DiskFile.py +++ b/gluster/swift/common/DiskFile.py @@ -24,7 +24,9 @@ from eventlet import sleep from contextlib import contextmanager from swift.common.utils import TRUE_VALUES, fallocate from swift.common.exceptions import DiskFileNotExist, DiskFileError -from gluster.swift.common.exceptions import GlusterFileSystemOSError + +from gluster.swift.common.exceptions import GlusterFileSystemOSError, \ + DiskFileNoSpace from gluster.swift.common.fs_utils import do_fstat, do_open, do_close, \ do_unlink, do_chown, os_path, do_fsync, do_fchown, do_stat from gluster.swift.common.utils import read_metadata, write_metadata, \ @@ -702,6 +704,11 @@ class Gluster_DiskFile(DiskFile): fd = do_open(tmppath, os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC) except GlusterFileSystemOSError as gerr: + if gerr.errno == errno.ENOSPC: + # Raise DiskFileNoSpace to be handled by upper layers + excp = DiskFileNoSpace() + excp.drive = os.path.basename(self.device_path) + raise excp if gerr.errno == errno.EEXIST: # Retry with a different random number. continue diff --git a/gluster/swift/common/exceptions.py b/gluster/swift/common/exceptions.py index 010ea24..ba2364e 100644 --- a/gluster/swift/common/exceptions.py +++ b/gluster/swift/common/exceptions.py @@ -44,3 +44,7 @@ class AlreadyExistsAsDir(GlusterfsException): class AlreadyExistsAsFile(GlusterfsException): pass + + +class DiskFileNoSpace(GlusterfsException): + pass diff --git a/gluster/swift/obj/server.py b/gluster/swift/obj/server.py index e7b9ff3..b3747ab 100644 --- a/gluster/swift/obj/server.py +++ b/gluster/swift/obj/server.py @@ -17,11 +17,13 @@ # Simply importing this monkey patches the constraint handling to fit our # needs -import gluster.swift.common.constraints # noqa -import gluster.swift.common.utils # noqa - from swift.obj import server +import gluster.swift.common.utils # noqa +import gluster.swift.common.constraints # noqa +from swift.common.utils import public, timing_stats from gluster.swift.common.DiskFile import Gluster_DiskFile +from gluster.swift.common.exceptions import DiskFileNoSpace +from swift.common.swob import HTTPInsufficientStorage # Monkey patch the object server module to use Gluster's DiskFile definition server.DiskFile = Gluster_DiskFile @@ -54,6 +56,15 @@ class ObjectController(server.ObjectController): """ return + @public + @timing_stats() + def PUT(self, request): + try: + return server.ObjectController.PUT(self, request) + except DiskFileNoSpace as err: + drive = err.drive + return HTTPInsufficientStorage(drive=drive, request=request) + def app_factory(global_conf, **local_conf): """paste.deploy app factory for creating WSGI object server apps""" diff --git a/test/unit/common/test_diskfile.py b/test/unit/common/test_diskfile.py index 76a9d1a..410f113 100644 --- a/test/unit/common/test_diskfile.py +++ b/test/unit/common/test_diskfile.py @@ -21,18 +21,21 @@ import errno import unittest import tempfile import shutil +import mock from mock import patch from hashlib import md5 -from swift.common.utils import normalize_timestamp -from swift.common.exceptions import DiskFileNotExist, DiskFileError -import gluster.swift.common.DiskFile + import gluster.swift.common.utils +import gluster.swift.common.DiskFile +from swift.common.utils import normalize_timestamp from gluster.swift.common.DiskFile import Gluster_DiskFile +from swift.common.exceptions import DiskFileNotExist, DiskFileError from gluster.swift.common.utils import DEFAULT_UID, DEFAULT_GID, X_TYPE, \ X_OBJECT_TYPE, DIR_OBJECT from test_utils import _initxattr, _destroyxattr from test.unit import FakeLogger +from gluster.swift.common.exceptions import * _metadata = {} @@ -569,6 +572,45 @@ class TestDiskFile(unittest.TestCase): finally: shutil.rmtree(td) + + def test_put_ENOSPC(self): + td = tempfile.mkdtemp() + the_cont = os.path.join(td, "vol0", "bar") + try: + os.makedirs(the_cont) + gdf = Gluster_DiskFile(td, "vol0", "p57", "ufo47", "bar", + "z", self.lg) + assert gdf._obj == "z" + assert gdf._obj_path == "" + assert gdf.name == "bar" + assert gdf.datadir == the_cont + assert gdf.data_file is None + + body = '1234\n' + etag = md5() + etag.update(body) + etag = etag.hexdigest() + metadata = { + 'X-Timestamp': '1234', + 'Content-Type': 'file', + 'ETag': etag, + 'Content-Length': '5', + } + def mock_open(*args, **kwargs): + raise OSError(errno.ENOSPC, os.strerror(errno.ENOSPC)) + + with mock.patch("os.open", mock_open): + try: + with gdf.mkstemp() as fd: + assert gdf.tmppath is not None + tmppath = gdf.tmppath + os.write(fd, body) + gdf.put(fd, metadata) + except DiskFileNoSpace: + pass + finally: + shutil.rmtree(td) + def test_put_obj_path(self): the_obj_path = os.path.join("b", "a") the_file = os.path.join(the_obj_path, "z") -- cgit