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:
parent
e367372f8b
commit
027951c102
@ -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
|
||||||
|
@ -44,3 +44,7 @@ class AlreadyExistsAsDir(GlusterfsException):
|
|||||||
|
|
||||||
class AlreadyExistsAsFile(GlusterfsException):
|
class AlreadyExistsAsFile(GlusterfsException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DiskFileNoSpace(GlusterfsException):
|
||||||
|
pass
|
||||||
|
@ -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"""
|
||||||
|
@ -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")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user