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 contextlib import contextmanager
from swift.common.utils import TRUE_VALUES, fallocate from swift.common.utils import TRUE_VALUES, fallocate
from swift.common.exceptions import DiskFileNotExist, DiskFileError 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, \ 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 do_unlink, do_chown, os_path, do_fsync, do_fchown, do_stat
from gluster.swift.common.utils import read_metadata, write_metadata, \ from gluster.swift.common.utils import read_metadata, write_metadata, \
@ -702,6 +704,11 @@ class Gluster_DiskFile(DiskFile):
fd = do_open(tmppath, fd = do_open(tmppath,
os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC) os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
except GlusterFileSystemOSError as gerr: 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: if gerr.errno == errno.EEXIST:
# Retry with a different random number. # Retry with a different random number.
continue continue

View File

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

View File

@ -17,11 +17,13 @@
# Simply importing this monkey patches the constraint handling to fit our # Simply importing this monkey patches the constraint handling to fit our
# needs # needs
import gluster.swift.common.constraints # noqa
import gluster.swift.common.utils # noqa
from swift.obj import server 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.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 # Monkey patch the object server module to use Gluster's DiskFile definition
server.DiskFile = Gluster_DiskFile server.DiskFile = Gluster_DiskFile
@ -54,6 +56,15 @@ class ObjectController(server.ObjectController):
""" """
return 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): def app_factory(global_conf, **local_conf):
"""paste.deploy app factory for creating WSGI object server apps""" """paste.deploy app factory for creating WSGI object server apps"""

View File

@ -21,18 +21,21 @@ import errno
import unittest import unittest
import tempfile import tempfile
import shutil import shutil
import mock
from mock import patch from mock import patch
from hashlib import md5 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.utils
import gluster.swift.common.DiskFile
from swift.common.utils import normalize_timestamp
from gluster.swift.common.DiskFile import Gluster_DiskFile 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, \ from gluster.swift.common.utils import DEFAULT_UID, DEFAULT_GID, X_TYPE, \
X_OBJECT_TYPE, DIR_OBJECT X_OBJECT_TYPE, DIR_OBJECT
from test_utils import _initxattr, _destroyxattr from test_utils import _initxattr, _destroyxattr
from test.unit import FakeLogger from test.unit import FakeLogger
from gluster.swift.common.exceptions import *
_metadata = {} _metadata = {}
@ -569,6 +572,45 @@ class TestDiskFile(unittest.TestCase):
finally: finally:
shutil.rmtree(td) 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): def test_put_obj_path(self):
the_obj_path = os.path.join("b", "a") the_obj_path = os.path.join("b", "a")
the_file = os.path.join(the_obj_path, "z") the_file = os.path.join(the_obj_path, "z")