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>
This commit is contained in:
Chetan Risbud 2013-07-31 19:36:32 +05:30 committed by Luis Pabon
parent e367372f8b
commit 027951c102
4 changed files with 71 additions and 7 deletions

View File

@ -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

View File

@ -44,3 +44,7 @@ class AlreadyExistsAsDir(GlusterfsException):
class AlreadyExistsAsFile(GlusterfsException):
pass
class DiskFileNoSpace(GlusterfsException):
pass

View File

@ -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"""

View File

@ -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")