UFO is openstack-swift (currently essex, 1.4.8), patched to add hooks for GlusterFS plug-ins. N.B. Gluster's changes to swift have been submitted to upstream, and when a new version of swift containing the hooks is released then this patched version will be removed from the GlusterFS packaging.
798 lines
34 KiB
Diff
798 lines
34 KiB
Diff
diff --git a/setup.py b/setup.py
|
|
index d195d34..b5b5ca2 100644
|
|
--- a/setup.py
|
|
+++ b/setup.py
|
|
@@ -1,5 +1,6 @@
|
|
#!/usr/bin/python
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -94,6 +95,7 @@ setup(
|
|
'tempurl=swift.common.middleware.tempurl:filter_factory',
|
|
'formpost=swift.common.middleware.formpost:filter_factory',
|
|
'name_check=swift.common.middleware.name_check:filter_factory',
|
|
+ 'gluster=swift.common.middleware.gluster:filter_factory',
|
|
],
|
|
},
|
|
)
|
|
diff --git a/swift/account/server.py b/swift/account/server.py
|
|
index 800b3c0..cb17970 100644
|
|
--- a/swift/account/server.py
|
|
+++ b/swift/account/server.py
|
|
@@ -1,4 +1,5 @@
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -31,7 +32,7 @@ import simplejson
|
|
|
|
from swift.common.db import AccountBroker
|
|
from swift.common.utils import get_logger, get_param, hash_path, \
|
|
- normalize_timestamp, split_path, storage_directory
|
|
+ normalize_timestamp, split_path, storage_directory, plugin_enabled
|
|
from swift.common.constraints import ACCOUNT_LISTING_LIMIT, \
|
|
check_mount, check_float, check_utf8
|
|
from swift.common.db_replicator import ReplicatorRpc
|
|
@@ -39,6 +40,8 @@ from swift.common.db_replicator import ReplicatorRpc
|
|
|
|
DATADIR = 'accounts'
|
|
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.DiskDir import DiskAccount
|
|
|
|
class AccountController(object):
|
|
"""WSGI controller for the account server."""
|
|
@@ -52,8 +55,12 @@ class AccountController(object):
|
|
self.mount_check, logger=self.logger)
|
|
self.auto_create_account_prefix = \
|
|
conf.get('auto_create_account_prefix') or '.'
|
|
+ self.fs_object = None
|
|
|
|
def _get_account_broker(self, drive, part, account):
|
|
+ if self.fs_object:
|
|
+ return DiskAccount(self.root, account, self.fs_object);
|
|
+
|
|
hsh = hash_path(account)
|
|
db_dir = storage_directory(DATADIR, part, hsh)
|
|
db_path = os.path.join(self.root, drive, db_dir, hsh + '.db')
|
|
@@ -121,9 +128,15 @@ class AccountController(object):
|
|
if broker.is_deleted():
|
|
return HTTPConflict(request=req)
|
|
metadata = {}
|
|
- metadata.update((key, (value, timestamp))
|
|
- for key, value in req.headers.iteritems()
|
|
- if key.lower().startswith('x-account-meta-'))
|
|
+ if not self.fs_object:
|
|
+ metadata.update((key, (value, timestamp))
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower().startswith('x-account-meta-'))
|
|
+ else:
|
|
+ metadata.update((key, value)
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower().startswith('x-account-meta-'))
|
|
+
|
|
if metadata:
|
|
broker.update_metadata(metadata)
|
|
if created:
|
|
@@ -153,6 +166,9 @@ class AccountController(object):
|
|
broker.stale_reads_ok = True
|
|
if broker.is_deleted():
|
|
return HTTPNotFound(request=req)
|
|
+ if self.fs_object and not self.fs_object.object_only:
|
|
+ broker.list_containers_iter(None, None,None,
|
|
+ None, None)
|
|
info = broker.get_info()
|
|
headers = {
|
|
'X-Account-Container-Count': info['container_count'],
|
|
@@ -164,9 +180,16 @@ class AccountController(object):
|
|
container_ts = broker.get_container_timestamp(container)
|
|
if container_ts is not None:
|
|
headers['X-Container-Timestamp'] = container_ts
|
|
- headers.update((key, value)
|
|
- for key, (value, timestamp) in broker.metadata.iteritems()
|
|
- if value != '')
|
|
+ if not self.fs_object:
|
|
+ headers.update((key, value)
|
|
+ for key, (value, timestamp) in broker.metadata.iteritems()
|
|
+ if value != '')
|
|
+ else:
|
|
+ headers.update((key, value)
|
|
+ for key, value in broker.metadata.iteritems()
|
|
+ if value != '')
|
|
+
|
|
+
|
|
return HTTPNoContent(request=req, headers=headers)
|
|
|
|
def GET(self, req):
|
|
@@ -190,9 +213,15 @@ class AccountController(object):
|
|
'X-Account-Bytes-Used': info['bytes_used'],
|
|
'X-Timestamp': info['created_at'],
|
|
'X-PUT-Timestamp': info['put_timestamp']}
|
|
- resp_headers.update((key, value)
|
|
- for key, (value, timestamp) in broker.metadata.iteritems()
|
|
- if value != '')
|
|
+ if not self.fs_object:
|
|
+ resp_headers.update((key, value)
|
|
+ for key, (value, timestamp) in broker.metadata.iteritems()
|
|
+ if value != '')
|
|
+ else:
|
|
+ resp_headers.update((key, value)
|
|
+ for key, value in broker.metadata.iteritems()
|
|
+ if value != '')
|
|
+
|
|
try:
|
|
prefix = get_param(req, 'prefix')
|
|
delimiter = get_param(req, 'delimiter')
|
|
@@ -224,6 +253,7 @@ class AccountController(object):
|
|
content_type='text/plain', request=req)
|
|
account_list = broker.list_containers_iter(limit, marker, end_marker,
|
|
prefix, delimiter)
|
|
+
|
|
if out_content_type == 'application/json':
|
|
json_pattern = ['"name":%s', '"count":%s', '"bytes":%s']
|
|
json_pattern = '{' + ','.join(json_pattern) + '}'
|
|
@@ -298,15 +328,29 @@ class AccountController(object):
|
|
return HTTPNotFound(request=req)
|
|
timestamp = normalize_timestamp(req.headers['x-timestamp'])
|
|
metadata = {}
|
|
- metadata.update((key, (value, timestamp))
|
|
- for key, value in req.headers.iteritems()
|
|
- if key.lower().startswith('x-account-meta-'))
|
|
+ if not self.fs_object:
|
|
+ metadata.update((key, (value, timestamp))
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower().startswith('x-account-meta-'))
|
|
+ else:
|
|
+ metadata.update((key, value)
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower().startswith('x-account-meta-'))
|
|
if metadata:
|
|
broker.update_metadata(metadata)
|
|
return HTTPNoContent(request=req)
|
|
|
|
+ def plugin(self, env):
|
|
+ if env.get('Gluster_enabled', False):
|
|
+ self.fs_object = env.get('fs_object')
|
|
+ self.root = env.get('root')
|
|
+ self.mount_check = False
|
|
+ else:
|
|
+ self.fs_object = None
|
|
+
|
|
def __call__(self, env, start_response):
|
|
start_time = time.time()
|
|
+ self.plugin(env)
|
|
req = Request(env)
|
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
|
if not check_utf8(req.path_info):
|
|
diff --git a/swift/common/middleware/gluster.py b/swift/common/middleware/gluster.py
|
|
new file mode 100644
|
|
index 0000000..341285d
|
|
--- /dev/null
|
|
+++ b/swift/common/middleware/gluster.py
|
|
@@ -0,0 +1,55 @@
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
+#
|
|
+# Licensed under the Apache License, Version 2.0 (the "License");
|
|
+# you may not use this file except in compliance with the License.
|
|
+# You may obtain a copy of the License at
|
|
+#
|
|
+# http://www.apache.org/licenses/LICENSE-2.0
|
|
+#
|
|
+# Unless required by applicable law or agreed to in writing, software
|
|
+# distributed under the License is distributed on an "AS IS" BASIS,
|
|
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
+# implied.
|
|
+# See the License for the specific language governing permissions and
|
|
+# limitations under the License.
|
|
+
|
|
+from swift.common.utils import get_logger, plugin_enabled
|
|
+from swift import plugins
|
|
+from ConfigParser import ConfigParser
|
|
+
|
|
+class Gluster_plugin(object):
|
|
+ """
|
|
+ Update the environment with keys that reflect Gluster_plugin enabled
|
|
+ """
|
|
+
|
|
+ def __init__(self, app, conf):
|
|
+ self.app = app
|
|
+ self.conf = conf
|
|
+ self.fs_name = 'Glusterfs'
|
|
+ self.logger = get_logger(conf, log_route='gluster')
|
|
+
|
|
+ def __call__(self, env, start_response):
|
|
+ if not plugin_enabled():
|
|
+ return self.app(env, start_response)
|
|
+ env['Gluster_enabled'] =True
|
|
+ fs_object = getattr(plugins, self.fs_name, False)
|
|
+ if not fs_object:
|
|
+ raise Exception('%s plugin not found', self.fs_name)
|
|
+
|
|
+ env['fs_object'] = fs_object()
|
|
+ fs_conf = ConfigParser()
|
|
+ if fs_conf.read('/etc/swift/fs.conf'):
|
|
+ try:
|
|
+ env['root'] = fs_conf.get ('DEFAULT', 'mount_path')
|
|
+ except NoSectionError, NoOptionError:
|
|
+ self.logger.exception(_('ERROR mount_path not present'))
|
|
+ return self.app(env, start_response)
|
|
+
|
|
+def filter_factory(global_conf, **local_conf):
|
|
+ """Returns a WSGI filter app for use with paste.deploy."""
|
|
+ conf = global_conf.copy()
|
|
+ conf.update(local_conf)
|
|
+
|
|
+ def gluster_filter(app):
|
|
+ return Gluster_plugin(app, conf)
|
|
+ return gluster_filter
|
|
diff --git a/swift/common/utils.py b/swift/common/utils.py
|
|
index 47edce8..03701ce 100644
|
|
--- a/swift/common/utils.py
|
|
+++ b/swift/common/utils.py
|
|
@@ -1,4 +1,5 @@
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -1138,3 +1139,11 @@ def streq_const_time(s1, s2):
|
|
for (a, b) in zip(s1, s2):
|
|
result |= ord(a) ^ ord(b)
|
|
return result == 0
|
|
+
|
|
+def plugin_enabled():
|
|
+ swift_conf = ConfigParser()
|
|
+ swift_conf.read(os.path.join('/etc/swift', 'swift.conf'))
|
|
+ try:
|
|
+ return swift_conf.get('DEFAULT', 'Enable_plugin', 'no') in TRUE_VALUES
|
|
+ except NoOptionError, NoSectionError:
|
|
+ return False
|
|
diff --git a/swift/container/server.py b/swift/container/server.py
|
|
index 8a18cfd..93943a3 100644
|
|
--- a/swift/container/server.py
|
|
+++ b/swift/container/server.py
|
|
@@ -1,4 +1,5 @@
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -31,7 +32,8 @@ from webob.exc import HTTPAccepted, HTTPBadRequest, HTTPConflict, \
|
|
|
|
from swift.common.db import ContainerBroker
|
|
from swift.common.utils import get_logger, get_param, hash_path, \
|
|
- normalize_timestamp, storage_directory, split_path, validate_sync_to
|
|
+ normalize_timestamp, storage_directory, split_path, validate_sync_to, \
|
|
+ plugin_enabled
|
|
from swift.common.constraints import CONTAINER_LISTING_LIMIT, \
|
|
check_mount, check_float, check_utf8
|
|
from swift.common.bufferedhttp import http_connect
|
|
@@ -40,6 +42,9 @@ from swift.common.db_replicator import ReplicatorRpc
|
|
|
|
DATADIR = 'containers'
|
|
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.DiskDir import DiskDir
|
|
+
|
|
|
|
class ContainerController(object):
|
|
"""WSGI Controller for the container server."""
|
|
@@ -62,6 +67,7 @@ class ContainerController(object):
|
|
ContainerBroker, self.mount_check, logger=self.logger)
|
|
self.auto_create_account_prefix = \
|
|
conf.get('auto_create_account_prefix') or '.'
|
|
+ self.fs_object = None
|
|
|
|
def _get_container_broker(self, drive, part, account, container):
|
|
"""
|
|
@@ -73,6 +79,11 @@ class ContainerController(object):
|
|
:param container: container name
|
|
:returns: ContainerBroker object
|
|
"""
|
|
+ if self.fs_object:
|
|
+ return DiskDir(self.root, drive, part, account,
|
|
+ container, self.logger,
|
|
+ fs_object = self.fs_object)
|
|
+
|
|
hsh = hash_path(account, container)
|
|
db_dir = storage_directory(DATADIR, part, hsh)
|
|
db_path = os.path.join(self.root, drive, db_dir, hsh + '.db')
|
|
@@ -211,10 +222,18 @@ class ContainerController(object):
|
|
if broker.is_deleted():
|
|
return HTTPConflict(request=req)
|
|
metadata = {}
|
|
- metadata.update((key, (value, timestamp))
|
|
- for key, value in req.headers.iteritems()
|
|
- if key.lower() in self.save_headers or
|
|
- key.lower().startswith('x-container-meta-'))
|
|
+ #Note: check the structure of req.headers
|
|
+ if not self.fs_object:
|
|
+ metadata.update((key, (value, timestamp))
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-'))
|
|
+ else:
|
|
+ metadata.update((key, value)
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-'))
|
|
+
|
|
if metadata:
|
|
if 'X-Container-Sync-To' in metadata:
|
|
if 'X-Container-Sync-To' not in broker.metadata or \
|
|
@@ -222,6 +241,7 @@ class ContainerController(object):
|
|
broker.metadata['X-Container-Sync-To'][0]:
|
|
broker.set_x_container_sync_points(-1, -1)
|
|
broker.update_metadata(metadata)
|
|
+
|
|
resp = self.account_update(req, account, container, broker)
|
|
if resp:
|
|
return resp
|
|
@@ -245,6 +265,11 @@ class ContainerController(object):
|
|
broker.stale_reads_ok = True
|
|
if broker.is_deleted():
|
|
return HTTPNotFound(request=req)
|
|
+
|
|
+ if self.fs_object and not self.fs_object.object_only:
|
|
+ broker.list_objects_iter(None, None, None, None,
|
|
+ None, None)
|
|
+
|
|
info = broker.get_info()
|
|
headers = {
|
|
'X-Container-Object-Count': info['object_count'],
|
|
@@ -252,10 +277,17 @@ class ContainerController(object):
|
|
'X-Timestamp': info['created_at'],
|
|
'X-PUT-Timestamp': info['put_timestamp'],
|
|
}
|
|
- headers.update((key, value)
|
|
- for key, (value, timestamp) in broker.metadata.iteritems()
|
|
- if value != '' and (key.lower() in self.save_headers or
|
|
- key.lower().startswith('x-container-meta-')))
|
|
+ if not self.fs_object:
|
|
+ headers.update((key, value)
|
|
+ for key, (value, timestamp) in broker.metadata.iteritems()
|
|
+ if value != '' and (key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-')))
|
|
+ else:
|
|
+ headers.update((key, value)
|
|
+ for key, value in broker.metadata.iteritems()
|
|
+ if value != '' and (key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-')))
|
|
+
|
|
return HTTPNoContent(request=req, headers=headers)
|
|
|
|
def GET(self, req):
|
|
@@ -268,6 +300,7 @@ class ContainerController(object):
|
|
request=req)
|
|
if self.mount_check and not check_mount(self.root, drive):
|
|
return Response(status='507 %s is not mounted' % drive)
|
|
+
|
|
broker = self._get_container_broker(drive, part, account, container)
|
|
broker.pending_timeout = 0.1
|
|
broker.stale_reads_ok = True
|
|
@@ -280,10 +313,17 @@ class ContainerController(object):
|
|
'X-Timestamp': info['created_at'],
|
|
'X-PUT-Timestamp': info['put_timestamp'],
|
|
}
|
|
- resp_headers.update((key, value)
|
|
- for key, (value, timestamp) in broker.metadata.iteritems()
|
|
- if value != '' and (key.lower() in self.save_headers or
|
|
- key.lower().startswith('x-container-meta-')))
|
|
+ if not self.fs_object:
|
|
+ resp_headers.update((key, value)
|
|
+ for key, (value, timestamp) in broker.metadata.iteritems()
|
|
+ if value != '' and (key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-')))
|
|
+ else:
|
|
+ resp_headers.update((key, value)
|
|
+ for key, value in broker.metadata.iteritems()
|
|
+ if value != '' and (key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-')))
|
|
+
|
|
try:
|
|
path = get_param(req, 'path')
|
|
prefix = get_param(req, 'prefix')
|
|
@@ -414,10 +454,17 @@ class ContainerController(object):
|
|
return HTTPNotFound(request=req)
|
|
timestamp = normalize_timestamp(req.headers['x-timestamp'])
|
|
metadata = {}
|
|
- metadata.update((key, (value, timestamp))
|
|
- for key, value in req.headers.iteritems()
|
|
- if key.lower() in self.save_headers or
|
|
- key.lower().startswith('x-container-meta-'))
|
|
+ if not self.fs_object:
|
|
+ metadata.update((key, (value, timestamp))
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-'))
|
|
+ else:
|
|
+ metadata.update((key, value)
|
|
+ for key, value in req.headers.iteritems()
|
|
+ if key.lower() in self.save_headers or
|
|
+ key.lower().startswith('x-container-meta-'))
|
|
+
|
|
if metadata:
|
|
if 'X-Container-Sync-To' in metadata:
|
|
if 'X-Container-Sync-To' not in broker.metadata or \
|
|
@@ -427,8 +474,19 @@ class ContainerController(object):
|
|
broker.update_metadata(metadata)
|
|
return HTTPNoContent(request=req)
|
|
|
|
+ def plugin(self, env):
|
|
+ if env.get('Gluster_enabled', False):
|
|
+ self.fs_object = env.get('fs_object')
|
|
+ if not self.fs_object:
|
|
+ raise NoneTypeError
|
|
+ self.root = env.get('root')
|
|
+ self.mount_check = False
|
|
+ else:
|
|
+ self.fs_object = None
|
|
+
|
|
def __call__(self, env, start_response):
|
|
start_time = time.time()
|
|
+ self.plugin(env)
|
|
req = Request(env)
|
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
|
if not check_utf8(req.path_info):
|
|
diff --git a/swift/obj/server.py b/swift/obj/server.py
|
|
index 9cca16b..a45daff 100644
|
|
--- a/swift/obj/server.py
|
|
+++ b/swift/obj/server.py
|
|
@@ -1,4 +1,5 @@
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -26,6 +27,7 @@ from hashlib import md5
|
|
from tempfile import mkstemp
|
|
from urllib import unquote
|
|
from contextlib import contextmanager
|
|
+from ConfigParser import ConfigParser
|
|
|
|
from webob import Request, Response, UTC
|
|
from webob.exc import HTTPAccepted, HTTPBadRequest, HTTPCreated, \
|
|
@@ -37,16 +39,23 @@ from eventlet import sleep, Timeout, tpool
|
|
|
|
from swift.common.utils import mkdirs, normalize_timestamp, \
|
|
storage_directory, hash_path, renamer, fallocate, \
|
|
- split_path, drop_buffer_cache, get_logger, write_pickle
|
|
+ split_path, drop_buffer_cache, get_logger, write_pickle, \
|
|
+ plugin_enabled
|
|
from swift.common.bufferedhttp import http_connect
|
|
-from swift.common.constraints import check_object_creation, check_mount, \
|
|
- check_float, check_utf8
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.constraints import check_object_creation
|
|
+ from swift.plugins.utils import X_TYPE, X_OBJECT_TYPE, FILE, DIR, MARKER_DIR, \
|
|
+ OBJECT, DIR_TYPE, FILE_TYPE
|
|
+else:
|
|
+ from swift.common.constraints import check_object_creation
|
|
+
|
|
+from swift.common.constraints import check_mount, check_float, check_utf8
|
|
+
|
|
from swift.common.exceptions import ConnectionTimeout, DiskFileError, \
|
|
DiskFileNotExist
|
|
from swift.obj.replicator import tpooled_get_hashes, invalidate_hash, \
|
|
quarantine_renamer
|
|
|
|
-
|
|
DATADIR = 'objects'
|
|
ASYNCDIR = 'async_pending'
|
|
PICKLE_PROTOCOL = 2
|
|
@@ -339,6 +348,9 @@ class DiskFile(object):
|
|
raise
|
|
raise DiskFileNotExist('Data File does not exist.')
|
|
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.DiskFile import Gluster_DiskFile
|
|
+
|
|
|
|
class ObjectController(object):
|
|
"""Implements the WSGI application for the Swift Object Server."""
|
|
@@ -377,6 +389,17 @@ class ObjectController(object):
|
|
'expiring_objects'
|
|
self.expiring_objects_container_divisor = \
|
|
int(conf.get('expiring_objects_container_divisor') or 86400)
|
|
+ self.fs_object = None
|
|
+
|
|
+ def get_DiskFile_obj(self, path, device, partition, account, container, obj,
|
|
+ logger, keep_data_fp=False, disk_chunk_size=65536):
|
|
+ if self.fs_object:
|
|
+ return Gluster_DiskFile(path, device, partition, account, container,
|
|
+ obj, logger, keep_data_fp,
|
|
+ disk_chunk_size, fs_object = self.fs_object);
|
|
+ else:
|
|
+ return DiskFile(path, device, partition, account, container,
|
|
+ obj, logger, keep_data_fp, disk_chunk_size)
|
|
|
|
def async_update(self, op, account, container, obj, host, partition,
|
|
contdevice, headers_out, objdevice):
|
|
@@ -493,7 +516,7 @@ class ObjectController(object):
|
|
content_type='text/plain')
|
|
if self.mount_check and not check_mount(self.devices, device):
|
|
return Response(status='507 %s is not mounted' % device)
|
|
- file = DiskFile(self.devices, device, partition, account, container,
|
|
+ file = self.get_DiskFile_obj(self.devices, device, partition, account, container,
|
|
obj, self.logger, disk_chunk_size=self.disk_chunk_size)
|
|
|
|
if 'X-Delete-At' in file.metadata and \
|
|
@@ -548,7 +571,7 @@ class ObjectController(object):
|
|
if new_delete_at and new_delete_at < time.time():
|
|
return HTTPBadRequest(body='X-Delete-At in past', request=request,
|
|
content_type='text/plain')
|
|
- file = DiskFile(self.devices, device, partition, account, container,
|
|
+ file = self.get_DiskFile_obj(self.devices, device, partition, account, container,
|
|
obj, self.logger, disk_chunk_size=self.disk_chunk_size)
|
|
orig_timestamp = file.metadata.get('X-Timestamp')
|
|
upload_expiration = time.time() + self.max_upload_time
|
|
@@ -580,12 +603,29 @@ class ObjectController(object):
|
|
if 'etag' in request.headers and \
|
|
request.headers['etag'].lower() != etag:
|
|
return HTTPUnprocessableEntity(request=request)
|
|
- metadata = {
|
|
- 'X-Timestamp': request.headers['x-timestamp'],
|
|
- 'Content-Type': request.headers['content-type'],
|
|
- 'ETag': etag,
|
|
- 'Content-Length': str(os.fstat(fd).st_size),
|
|
- }
|
|
+ content_type = request.headers['content-type']
|
|
+ if self.fs_object and not content_type:
|
|
+ content_type = FILE_TYPE
|
|
+ if not self.fs_object:
|
|
+ metadata = {
|
|
+ 'X-Timestamp': request.headers['x-timestamp'],
|
|
+ 'Content-Type': request.headers['content-type'],
|
|
+ 'ETag': etag,
|
|
+ 'Content-Length': str(os.fstat(fd).st_size),
|
|
+ }
|
|
+ else:
|
|
+ metadata = {
|
|
+ 'X-Timestamp': request.headers['x-timestamp'],
|
|
+ 'Content-Type': request.headers['content-type'],
|
|
+ 'ETag': etag,
|
|
+ 'Content-Length': str(os.fstat(fd).st_size),
|
|
+ X_TYPE: OBJECT,
|
|
+ X_OBJECT_TYPE: FILE,
|
|
+ }
|
|
+
|
|
+ if self.fs_object and \
|
|
+ request.headers['content-type'].lower() == DIR_TYPE:
|
|
+ metadata.update({X_OBJECT_TYPE: MARKER_DIR})
|
|
metadata.update(val for val in request.headers.iteritems()
|
|
if val[0].lower().startswith('x-object-meta-') and
|
|
len(val[0]) > 14)
|
|
@@ -612,7 +652,7 @@ class ObjectController(object):
|
|
'x-timestamp': file.metadata['X-Timestamp'],
|
|
'x-etag': file.metadata['ETag'],
|
|
'x-trans-id': request.headers.get('x-trans-id', '-')},
|
|
- device)
|
|
+ (self.fs_object and account) or device)
|
|
resp = HTTPCreated(request=request, etag=etag)
|
|
return resp
|
|
|
|
@@ -626,9 +666,9 @@ class ObjectController(object):
|
|
content_type='text/plain')
|
|
if self.mount_check and not check_mount(self.devices, device):
|
|
return Response(status='507 %s is not mounted' % device)
|
|
- file = DiskFile(self.devices, device, partition, account, container,
|
|
- obj, self.logger, keep_data_fp=True,
|
|
- disk_chunk_size=self.disk_chunk_size)
|
|
+ file = self.get_DiskFile_obj(self.devices, device, partition, account, container,
|
|
+ obj, self.logger, keep_data_fp=True,
|
|
+ disk_chunk_size=self.disk_chunk_size)
|
|
if file.is_deleted() or ('X-Delete-At' in file.metadata and
|
|
int(file.metadata['X-Delete-At']) <= time.time()):
|
|
if request.headers.get('if-match') == '*':
|
|
@@ -702,7 +742,7 @@ class ObjectController(object):
|
|
return resp
|
|
if self.mount_check and not check_mount(self.devices, device):
|
|
return Response(status='507 %s is not mounted' % device)
|
|
- file = DiskFile(self.devices, device, partition, account, container,
|
|
+ file = self.get_DiskFile_obj(self.devices, device, partition, account, container,
|
|
obj, self.logger, disk_chunk_size=self.disk_chunk_size)
|
|
if file.is_deleted() or ('X-Delete-At' in file.metadata and
|
|
int(file.metadata['X-Delete-At']) <= time.time()):
|
|
@@ -744,7 +784,7 @@ class ObjectController(object):
|
|
if self.mount_check and not check_mount(self.devices, device):
|
|
return Response(status='507 %s is not mounted' % device)
|
|
response_class = HTTPNoContent
|
|
- file = DiskFile(self.devices, device, partition, account, container,
|
|
+ file = self.get_DiskFile_obj(self.devices, device, partition, account, container,
|
|
obj, self.logger, disk_chunk_size=self.disk_chunk_size)
|
|
if 'x-if-delete-at' in request.headers and \
|
|
int(request.headers['x-if-delete-at']) != \
|
|
@@ -797,9 +837,18 @@ class ObjectController(object):
|
|
raise hashes
|
|
return Response(body=pickle.dumps(hashes))
|
|
|
|
+ def plugin(self, env):
|
|
+ if env.get('Gluster_enabled', False):
|
|
+ self.fs_object = env.get('fs_object')
|
|
+ self.devices = env.get('root')
|
|
+ self.mount_check = False
|
|
+ else:
|
|
+ self.fs_object = None
|
|
+
|
|
def __call__(self, env, start_response):
|
|
"""WSGI Application entry point for the Swift Object Server."""
|
|
start_time = time.time()
|
|
+ self.plugin(env)
|
|
req = Request(env)
|
|
self.logger.txn_id = req.headers.get('x-trans-id', None)
|
|
if not check_utf8(req.path_info):
|
|
diff --git a/swift/proxy/server.py b/swift/proxy/server.py
|
|
index 17613b8..d277d28 100644
|
|
--- a/swift/proxy/server.py
|
|
+++ b/swift/proxy/server.py
|
|
@@ -1,4 +1,5 @@
|
|
# Copyright (c) 2010-2012 OpenStack, LLC.
|
|
+# Copyright (c) 2011 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
@@ -53,11 +54,20 @@ from webob import Request, Response
|
|
|
|
from swift.common.ring import Ring
|
|
from swift.common.utils import cache_from_env, ContextPool, get_logger, \
|
|
- get_remote_client, normalize_timestamp, split_path, TRUE_VALUES
|
|
+ get_remote_client, normalize_timestamp, split_path, TRUE_VALUES, \
|
|
+ plugin_enabled
|
|
from swift.common.bufferedhttp import http_connect
|
|
-from swift.common.constraints import check_metadata, check_object_creation, \
|
|
- check_utf8, CONTAINER_LISTING_LIMIT, MAX_ACCOUNT_NAME_LENGTH, \
|
|
- MAX_CONTAINER_NAME_LENGTH, MAX_FILE_SIZE
|
|
+
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.constraints import check_object_creation, \
|
|
+ MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, MAX_FILE_SIZE
|
|
+else:
|
|
+ from swift.common.constraints import check_object_creation, \
|
|
+ MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, MAX_FILE_SIZE
|
|
+
|
|
+from swift.common.constraints import check_metadata, check_utf8, \
|
|
+ CONTAINER_LISTING_LIMIT
|
|
+
|
|
from swift.common.exceptions import ChunkReadTimeout, \
|
|
ChunkWriteTimeout, ConnectionTimeout
|
|
|
|
diff --git a/test/__init__.py b/test/__init__.py
|
|
index ef2ce31..363a051 100644
|
|
--- a/test/__init__.py
|
|
+++ b/test/__init__.py
|
|
@@ -6,8 +6,16 @@ import sys
|
|
import os
|
|
from ConfigParser import MissingSectionHeaderError
|
|
from StringIO import StringIO
|
|
-
|
|
from swift.common.utils import readconf
|
|
+from swift.common.utils import plugin_enabled
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.constraints import MAX_OBJECT_NAME_LENGTH, \
|
|
+ MAX_CONTAINER_NAME_LENGTH, MAX_ACCOUNT_NAME_LENGTH, \
|
|
+ MAX_FILE_SIZE
|
|
+else:
|
|
+ from swift.common.constraints import MAX_OBJECT_NAME_LENGTH, \
|
|
+ MAX_CONTAINER_NAME_LENGTH, MAX_ACCOUNT_NAME_LENGTH, \
|
|
+ MAX_FILE_SIZE
|
|
|
|
setattr(__builtin__, '_', lambda x: x)
|
|
|
|
diff --git a/test/functional/tests.py b/test/functional/tests.py
|
|
index b25b4fd..8d12f58 100644
|
|
--- a/test/functional/tests.py
|
|
+++ b/test/functional/tests.py
|
|
@@ -31,6 +31,16 @@ import urllib
|
|
from test import get_config
|
|
from swift import Account, AuthenticationFailed, Connection, Container, \
|
|
File, ResponseError
|
|
+from test import plugin_enabled
|
|
+if plugin_enabled():
|
|
+ from test import MAX_OBJECT_NAME_LENGTH, \
|
|
+ MAX_CONTAINER_NAME_LENGTH, MAX_ACCOUNT_NAME_LENGTH, \
|
|
+ MAX_FILE_SIZE
|
|
+else:
|
|
+ from test import MAX_OBJECT_NAME_LENGTH, \
|
|
+ MAX_CONTAINER_NAME_LENGTH, MAX_ACCOUNT_NAME_LENGTH, \
|
|
+ MAX_FILE_SIZE
|
|
+
|
|
|
|
config = get_config()
|
|
|
|
@@ -361,7 +371,7 @@ class TestContainer(Base):
|
|
set_up = False
|
|
|
|
def testContainerNameLimit(self):
|
|
- limit = 256
|
|
+ limit = MAX_CONTAINER_NAME_LENGTH
|
|
|
|
for l in (limit-100, limit-10, limit-1, limit,
|
|
limit+1, limit+10, limit+100):
|
|
@@ -949,7 +959,7 @@ class TestFile(Base):
|
|
self.assert_status(404)
|
|
|
|
def testNameLimit(self):
|
|
- limit = 1024
|
|
+ limit = MAX_OBJECT_NAME_LENGTH
|
|
|
|
for l in (1, 10, limit/2, limit-1, limit, limit+1, limit*2):
|
|
file = self.env.container.file('a'*l)
|
|
@@ -1093,7 +1103,7 @@ class TestFile(Base):
|
|
self.assert_(file.read(hdrs={'Range': r}) == data[0:1000])
|
|
|
|
def testFileSizeLimit(self):
|
|
- limit = 5*2**30 + 2
|
|
+ limit = MAX_FILE_SIZE
|
|
tsecs = 3
|
|
|
|
for i in (limit-100, limit-10, limit-1, limit, limit+1, limit+10,
|
|
diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py
|
|
index 075700e..5b6f32d 100644
|
|
--- a/test/unit/obj/test_server.py
|
|
+++ b/test/unit/obj/test_server.py
|
|
@@ -1355,7 +1355,7 @@ class TestObjectController(unittest.TestCase):
|
|
|
|
def test_max_object_name_length(self):
|
|
timestamp = normalize_timestamp(time())
|
|
- req = Request.blank('/sda1/p/a/c/' + ('1' * 1024),
|
|
+ req = Request.blank('/sda1/p/a/c/' + ('1' * MAX_OBJECT_NAME_LENGTH),
|
|
environ={'REQUEST_METHOD': 'PUT'},
|
|
headers={'X-Timestamp': timestamp,
|
|
'Content-Length': '4',
|
|
diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py
|
|
index 364370e..c17fe59 100644
|
|
--- a/test/unit/proxy/test_server.py
|
|
+++ b/test/unit/proxy/test_server.py
|
|
@@ -21,7 +21,6 @@ import os
|
|
import sys
|
|
import unittest
|
|
from nose import SkipTest
|
|
-from ConfigParser import ConfigParser
|
|
from contextlib import contextmanager
|
|
from cStringIO import StringIO
|
|
from gzip import GzipFile
|
|
@@ -44,8 +43,18 @@ from swift.account import server as account_server
|
|
from swift.container import server as container_server
|
|
from swift.obj import server as object_server
|
|
from swift.common import ring
|
|
-from swift.common.constraints import MAX_META_NAME_LENGTH, \
|
|
- MAX_META_VALUE_LENGTH, MAX_META_COUNT, MAX_META_OVERALL_SIZE, MAX_FILE_SIZE
|
|
+from swift.common.utils import plugin_enabled
|
|
+if plugin_enabled():
|
|
+ from swift.plugins.constraints import MAX_META_NAME_LENGTH, \
|
|
+ MAX_META_VALUE_LENGTH, MAX_META_COUNT, MAX_META_OVERALL_SIZE, \
|
|
+ MAX_FILE_SIZE, MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, \
|
|
+ MAX_OBJECT_NAME_LENGTH
|
|
+else:
|
|
+ from swift.plugins.constraints import MAX_META_NAME_LENGTH, \
|
|
+ MAX_META_VALUE_LENGTH, MAX_META_COUNT, MAX_META_OVERALL_SIZE, \
|
|
+ MAX_FILE_SIZE, MAX_ACCOUNT_NAME_LENGTH, MAX_CONTAINER_NAME_LENGTH, \
|
|
+ MAX_OBJECT_NAME_LENGTH
|
|
+
|
|
from swift.common.utils import mkdirs, normalize_timestamp, NullLogger
|
|
from swift.common.wsgi import monkey_patch_mimetools
|
|
|
|
@@ -3207,7 +3216,8 @@ class TestContainerController(unittest.TestCase):
|
|
def test_PUT_max_container_name_length(self):
|
|
with save_globals():
|
|
controller = proxy_server.ContainerController(self.app, 'account',
|
|
- '1' * 256)
|
|
+ '1' *
|
|
+ MAX_CONTAINER_NAME_LENGTH,)
|
|
self.assert_status_map(controller.PUT,
|
|
(200, 200, 200, 201, 201, 201), 201,
|
|
missing_container=True)
|
|
@@ -3813,7 +3823,8 @@ class TestAccountController(unittest.TestCase):
|
|
def test_PUT_max_account_name_length(self):
|
|
with save_globals():
|
|
self.app.allow_account_management = True
|
|
- controller = proxy_server.AccountController(self.app, '1' * 256)
|
|
+ controller = proxy_server.AccountController(self.app, '1' *
|
|
+ MAX_ACCOUNT_NAME_LENGTH)
|
|
self.assert_status_map(controller.PUT, (201, 201, 201), 201)
|
|
controller = proxy_server.AccountController(self.app, '2' * 257)
|
|
self.assert_status_map(controller.PUT, (201, 201, 201), 400)
|