diff options
| author | Chetan Risbud <crisbud@redhat.com> | 2013-07-31 19:36:32 +0530 | 
|---|---|---|
| committer | Luis Pabon <lpabon@redhat.com> | 2013-08-13 12:34:48 -0700 | 
| commit | 32ce873516af0baf76cfb9fea831434b672f3516 (patch) | |
| tree | 3075eadd43a1105fd10033550ea37557f6f59418 | |
| parent | f54f00dc313d52414230e3c05dfa383ad7e7a8ce (diff) | |
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 <crisbud@redhat.com>
Reviewed-on: http://review.gluster.org/5362
Reviewed-by: Peter Portante <pportant@redhat.com>
Reviewed-by: Luis Pabon <lpabon@redhat.com>
Tested-by: Luis Pabon <lpabon@redhat.com>
Reviewed-on: http://review.gluster.org/5570
| -rw-r--r-- | gluster/swift/common/DiskFile.py | 9 | ||||
| -rw-r--r-- | gluster/swift/common/exceptions.py | 4 | ||||
| -rw-r--r-- | gluster/swift/obj/server.py | 17 | ||||
| -rw-r--r-- | 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")  | 
