Merged from trunk
This commit is contained in:
@@ -25,7 +25,7 @@ from optparse import OptionParser
|
|||||||
from sys import argv, exit
|
from sys import argv, exit
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
|
|
||||||
from swift.common.client import Connection
|
from swift.common.client import Connection, ClientException
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -65,7 +65,17 @@ if __name__ == '__main__':
|
|||||||
while True:
|
while True:
|
||||||
if options.verbose:
|
if options.verbose:
|
||||||
print 'GET %s?marker=%s' % (container, marker)
|
print 'GET %s?marker=%s' % (container, marker)
|
||||||
objs = conn.get_container(container, marker=marker)[1]
|
try:
|
||||||
|
objs = conn.get_container(container, marker=marker)[1]
|
||||||
|
except ClientException, e:
|
||||||
|
if e.http_status == 404:
|
||||||
|
print 'Container %s not found' % (container)
|
||||||
|
print 'swauth-prep needs to be rerun'
|
||||||
|
exit()
|
||||||
|
else:
|
||||||
|
print 'Object listing on container %s failed with ' \
|
||||||
|
'status code %d' % (container, e.http_status)
|
||||||
|
break
|
||||||
if objs:
|
if objs:
|
||||||
marker = objs[-1]['name']
|
marker = objs[-1]['name']
|
||||||
else:
|
else:
|
||||||
@@ -90,7 +100,13 @@ if __name__ == '__main__':
|
|||||||
(container, obj['name'],
|
(container, obj['name'],
|
||||||
time() - detail['expires'])
|
time() - detail['expires'])
|
||||||
print 'DELETE %s/%s' % (container, obj['name'])
|
print 'DELETE %s/%s' % (container, obj['name'])
|
||||||
conn.delete_object(container, obj['name'])
|
try:
|
||||||
|
conn.delete_object(container, obj['name'])
|
||||||
|
except ClientException, e:
|
||||||
|
if e.http_status != 404:
|
||||||
|
print 'DELETE of %s/%s failed with status ' \
|
||||||
|
'code %d' % (container, obj['name'],
|
||||||
|
e.http_status)
|
||||||
elif options.verbose:
|
elif options.verbose:
|
||||||
print "%s/%s won't expire for %ds; skipping" % \
|
print "%s/%s won't expire for %ds; skipping" % \
|
||||||
(container, obj['name'],
|
(container, obj['name'],
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ virtual machine will emulate running a four node Swift cluster.
|
|||||||
|
|
||||||
* Get the *Ubuntu 10.04 LTS (Lucid Lynx)* server image:
|
* Get the *Ubuntu 10.04 LTS (Lucid Lynx)* server image:
|
||||||
|
|
||||||
- Ubuntu Server ISO: http://releases.ubuntu.com/10.04/ubuntu-10.04.1-server-amd64.iso (682 MB)
|
- Ubuntu Server ISO: http://releases.ubuntu.com/lucid/ubuntu-10.04.2-server-amd64.iso (717 MB)
|
||||||
- Ubuntu Live/Install: http://cdimage.ubuntu.com/releases/10.04/release/ubuntu-10.04-dvd-amd64.iso (4.1 GB)
|
- Ubuntu Live/Install: http://cdimage.ubuntu.com/releases/lucid/release/ubuntu-10.04.2-dvd-amd64.iso (4.2 GB)
|
||||||
- Ubuntu Mirrors: https://launchpad.net/ubuntu/+cdmirrors
|
- Ubuntu Mirrors: https://launchpad.net/ubuntu/+cdmirrors
|
||||||
|
|
||||||
* Create guest virtual machine from the Ubuntu image.
|
* Create guest virtual machine from the Ubuntu image.
|
||||||
@@ -70,6 +70,7 @@ Using a loopback device for storage
|
|||||||
|
|
||||||
If you want to use a loopback device instead of another partition, follow these instructions.
|
If you want to use a loopback device instead of another partition, follow these instructions.
|
||||||
|
|
||||||
|
#. `mkdir /srv`
|
||||||
#. `dd if=/dev/zero of=/srv/swift-disk bs=1024 count=0 seek=1000000`
|
#. `dd if=/dev/zero of=/srv/swift-disk bs=1024 count=0 seek=1000000`
|
||||||
(modify seek to make a larger or smaller partition)
|
(modify seek to make a larger or smaller partition)
|
||||||
#. `mkfs.xfs -i size=1024 /srv/swift-disk`
|
#. `mkfs.xfs -i size=1024 /srv/swift-disk`
|
||||||
@@ -79,7 +80,6 @@ If you want to use a loopback device instead of another partition, follow these
|
|||||||
#. `mount /mnt/sdb1`
|
#. `mount /mnt/sdb1`
|
||||||
#. `mkdir /mnt/sdb1/1 /mnt/sdb1/2 /mnt/sdb1/3 /mnt/sdb1/4`
|
#. `mkdir /mnt/sdb1/1 /mnt/sdb1/2 /mnt/sdb1/3 /mnt/sdb1/4`
|
||||||
#. `chown <your-user-name>:<your-group-name> /mnt/sdb1/*`
|
#. `chown <your-user-name>:<your-group-name> /mnt/sdb1/*`
|
||||||
#. `mkdir /srv`
|
|
||||||
#. `for x in {1..4}; do ln -s /mnt/sdb1/$x /srv/$x; done`
|
#. `for x in {1..4}; do ln -s /mnt/sdb1/$x /srv/$x; done`
|
||||||
#. `mkdir -p /etc/swift/object-server /etc/swift/container-server /etc/swift/account-server /srv/1/node/sdb1 /srv/2/node/sdb2 /srv/3/node/sdb3 /srv/4/node/sdb4 /var/run/swift`
|
#. `mkdir -p /etc/swift/object-server /etc/swift/container-server /etc/swift/account-server /srv/1/node/sdb1 /srv/2/node/sdb2 /srv/3/node/sdb3 /srv/4/node/sdb4 /var/run/swift`
|
||||||
#. `chown -R <your-user-name>:<your-group-name> /etc/swift /srv/[1-4]/ /var/run/swift` -- **Make sure to include the trailing slash after /srv/[1-4]/**
|
#. `chown -R <your-user-name>:<your-group-name> /etc/swift /srv/[1-4]/ /var/run/swift` -- **Make sure to include the trailing slash after /srv/[1-4]/**
|
||||||
@@ -563,7 +563,9 @@ Sample configuration files are provided with all defaults in line-by-line commen
|
|||||||
Setting up scripts for running Swift
|
Setting up scripts for running Swift
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
#. Create `~/bin/resetswift.` If you are using a loopback device substitute `/dev/sdb1` with `/srv/swift-disk`::
|
#. Create `~/bin/resetswift.`
|
||||||
|
If you are using a loopback device substitute `/dev/sdb1` with `/srv/swift-disk`.
|
||||||
|
If you did not set up rsyslog for individual logging, remove the `find /var/log/swift...` line::
|
||||||
|
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,16 @@ class BenchDELETE(Bench):
|
|||||||
self.total = len(names)
|
self.total = len(names)
|
||||||
self.msg = 'DEL'
|
self.msg = 'DEL'
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
Bench.run(self)
|
||||||
|
for container in self.containers:
|
||||||
|
try:
|
||||||
|
client.delete_container(self.url, self.token, container)
|
||||||
|
except client.ClientException, e:
|
||||||
|
if e.http_status != 409:
|
||||||
|
self._log_status("Unable to delete container '%s'. " \
|
||||||
|
"Got http status '%d'." % (container, e.http_status))
|
||||||
|
|
||||||
def _run(self, thread):
|
def _run(self, thread):
|
||||||
if time.time() - self.heartbeat >= 15:
|
if time.time() - self.heartbeat >= 15:
|
||||||
self.heartbeat = time.time()
|
self.heartbeat = time.time()
|
||||||
|
|||||||
@@ -776,7 +776,7 @@ def readconf(conf, section_name=None, log_name=None, defaults=None):
|
|||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
||||||
def write_pickle(obj, dest, tmp):
|
def write_pickle(obj, dest, tmp, pickle_protocol=0):
|
||||||
"""
|
"""
|
||||||
Ensure that a pickle file gets written to disk. The file
|
Ensure that a pickle file gets written to disk. The file
|
||||||
is first written to a tmp location, ensure it is synced to disk, then
|
is first written to a tmp location, ensure it is synced to disk, then
|
||||||
@@ -785,10 +785,11 @@ def write_pickle(obj, dest, tmp):
|
|||||||
:param obj: python object to be pickled
|
:param obj: python object to be pickled
|
||||||
:param dest: path of final destination file
|
:param dest: path of final destination file
|
||||||
:param tmp: path to tmp to use
|
:param tmp: path to tmp to use
|
||||||
|
:param pickle_protocol: protocol to pickle the obj with, defaults to 0
|
||||||
"""
|
"""
|
||||||
fd, tmppath = mkstemp(dir=tmp)
|
fd, tmppath = mkstemp(dir=tmp, suffix='.tmp')
|
||||||
with os.fdopen(fd, 'wb') as fo:
|
with os.fdopen(fd, 'wb') as fo:
|
||||||
pickle.dump(obj, fo)
|
pickle.dump(obj, fo, pickle_protocol)
|
||||||
fo.flush()
|
fo.flush()
|
||||||
os.fsync(fd)
|
os.fsync(fd)
|
||||||
renamer(tmppath, dest)
|
renamer(tmppath, dest)
|
||||||
@@ -990,6 +991,8 @@ def get_remote_client(req):
|
|||||||
if not client and 'x-forwarded-for' in req.headers:
|
if not client and 'x-forwarded-for' in req.headers:
|
||||||
# remote host for other lbs
|
# remote host for other lbs
|
||||||
client = req.headers['x-forwarded-for'].split(',')[0].strip()
|
client = req.headers['x-forwarded-for'].split(',')[0].strip()
|
||||||
|
if not client:
|
||||||
|
client = req.remote_addr
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,6 @@ import mimetools
|
|||||||
import eventlet
|
import eventlet
|
||||||
from eventlet import greenio, GreenPool, sleep, wsgi, listen
|
from eventlet import greenio, GreenPool, sleep, wsgi, listen
|
||||||
from paste.deploy import loadapp, appconfig
|
from paste.deploy import loadapp, appconfig
|
||||||
|
|
||||||
# Hook to ensure connection resets don't blow up our servers.
|
|
||||||
# Remove with next release of Eventlet that has it in the set already.
|
|
||||||
from errno import ECONNRESET
|
|
||||||
wsgi.ACCEPT_ERRNO.add(ECONNRESET)
|
|
||||||
|
|
||||||
from eventlet.green import socket, ssl
|
from eventlet.green import socket, ssl
|
||||||
|
|
||||||
from swift.common.utils import get_logger, drop_privileges, \
|
from swift.common.utils import get_logger, drop_privileges, \
|
||||||
@@ -124,8 +118,8 @@ def run_wsgi(conf_file, app_section, *args, **kwargs):
|
|||||||
# remaining tasks should not require elevated privileges
|
# remaining tasks should not require elevated privileges
|
||||||
drop_privileges(conf.get('user', 'swift'))
|
drop_privileges(conf.get('user', 'swift'))
|
||||||
|
|
||||||
# finally after binding to ports and privilege drop, run app __init__ code
|
# Ensure the application can be loaded before proceeding.
|
||||||
app = loadapp('config:%s' % conf_file, global_conf={'log_name': log_name})
|
loadapp('config:%s' % conf_file, global_conf={'log_name': log_name})
|
||||||
|
|
||||||
# redirect errors to logger and close stdio
|
# redirect errors to logger and close stdio
|
||||||
capture_stdio(logger)
|
capture_stdio(logger)
|
||||||
@@ -135,6 +129,8 @@ def run_wsgi(conf_file, app_section, *args, **kwargs):
|
|||||||
eventlet.hubs.use_hub('poll')
|
eventlet.hubs.use_hub('poll')
|
||||||
eventlet.patcher.monkey_patch(all=False, socket=True)
|
eventlet.patcher.monkey_patch(all=False, socket=True)
|
||||||
monkey_patch_mimetools()
|
monkey_patch_mimetools()
|
||||||
|
app = loadapp('config:%s' % conf_file,
|
||||||
|
global_conf={'log_name': log_name})
|
||||||
pool = GreenPool(size=1024)
|
pool = GreenPool(size=1024)
|
||||||
try:
|
try:
|
||||||
wsgi.server(sock, app, NullLogger(), custom_pool=pool)
|
wsgi.server(sock, app, NullLogger(), custom_pool=pool)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from eventlet.support.greenlets import GreenletExit
|
|||||||
|
|
||||||
from swift.common.ring import Ring
|
from swift.common.ring import Ring
|
||||||
from swift.common.utils import whataremyips, unlink_older_than, lock_path, \
|
from swift.common.utils import whataremyips, unlink_older_than, lock_path, \
|
||||||
renamer, compute_eta, get_logger
|
compute_eta, get_logger, write_pickle
|
||||||
from swift.common.bufferedhttp import http_connect
|
from swift.common.bufferedhttp import http_connect
|
||||||
from swift.common.daemon import Daemon
|
from swift.common.daemon import Daemon
|
||||||
|
|
||||||
@@ -105,9 +105,7 @@ def invalidate_hash(suffix_dir):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
hashes[suffix] = None
|
hashes[suffix] = None
|
||||||
with open(hashes_file + '.tmp', 'wb') as fp:
|
write_pickle(hashes, hashes_file, partition_dir, PICKLE_PROTOCOL)
|
||||||
pickle.dump(hashes, fp, PICKLE_PROTOCOL)
|
|
||||||
renamer(hashes_file + '.tmp', hashes_file)
|
|
||||||
|
|
||||||
|
|
||||||
def get_hashes(partition_dir, recalculate=[], do_listdir=False,
|
def get_hashes(partition_dir, recalculate=[], do_listdir=False,
|
||||||
@@ -157,9 +155,7 @@ def get_hashes(partition_dir, recalculate=[], do_listdir=False,
|
|||||||
modified = True
|
modified = True
|
||||||
sleep()
|
sleep()
|
||||||
if modified:
|
if modified:
|
||||||
with open(hashes_file + '.tmp', 'wb') as fp:
|
write_pickle(hashes, hashes_file, partition_dir, PICKLE_PROTOCOL)
|
||||||
pickle.dump(hashes, fp, PICKLE_PROTOCOL)
|
|
||||||
renamer(hashes_file + '.tmp', hashes_file)
|
|
||||||
return hashed, hashes
|
return hashed, hashes
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,17 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import os
|
||||||
from os import kill
|
from os import kill
|
||||||
from signal import SIGTERM
|
from signal import SIGTERM
|
||||||
from subprocess import Popen
|
from subprocess import Popen
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
import eventlet
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
from swift.common import client
|
from swift.common import client
|
||||||
|
from swift.common.utils import hash_path, readconf
|
||||||
|
|
||||||
from test.probe.common import get_to_final_state, kill_pids, reset_environment
|
from test.probe.common import get_to_final_state, kill_pids, reset_environment
|
||||||
|
|
||||||
@@ -316,6 +320,61 @@ class TestContainerFailures(unittest.TestCase):
|
|||||||
self.assert_(object2 in [o['name'] for o in
|
self.assert_(object2 in [o['name'] for o in
|
||||||
client.get_container(self.url, self.token, container)[1]])
|
client.get_container(self.url, self.token, container)[1]])
|
||||||
|
|
||||||
|
def _get_db_file_path(self, obj_dir):
|
||||||
|
files = sorted(os.listdir(obj_dir), reverse=True)
|
||||||
|
for file in files:
|
||||||
|
if file.endswith('db'):
|
||||||
|
return os.path.join(obj_dir, file)
|
||||||
|
|
||||||
|
def _get_container_db_files(self, container):
|
||||||
|
opart, onodes = self.container_ring.get_nodes(self.account, container)
|
||||||
|
onode = onodes[0]
|
||||||
|
db_files = []
|
||||||
|
for onode in onodes:
|
||||||
|
node_id = (onode['port'] - 6000) / 10
|
||||||
|
device = onode['device']
|
||||||
|
hash_str = hash_path(self.account, container)
|
||||||
|
server_conf = readconf('/etc/swift/container-server/%s.conf' %
|
||||||
|
node_id)
|
||||||
|
devices = server_conf['app:container-server']['devices']
|
||||||
|
obj_dir = '%s/%s/containers/%s/%s/%s/' % (devices,
|
||||||
|
device, opart,
|
||||||
|
hash_str[-3:], hash_str)
|
||||||
|
db_files.append(self._get_db_file_path(obj_dir))
|
||||||
|
|
||||||
|
return db_files
|
||||||
|
|
||||||
|
def test_locked_container_dbs(self):
|
||||||
|
|
||||||
|
def run_test(num_locks, catch_503):
|
||||||
|
container = 'container-%s' % uuid4()
|
||||||
|
client.put_container(self.url, self.token, container)
|
||||||
|
db_files = self._get_container_db_files(container)
|
||||||
|
db_conns = []
|
||||||
|
for i in range(num_locks):
|
||||||
|
db_conn = sqlite3.connect(db_files[i])
|
||||||
|
db_conn.execute('begin exclusive transaction')
|
||||||
|
db_conns.append(db_conn)
|
||||||
|
if catch_503:
|
||||||
|
try:
|
||||||
|
client.delete_container(self.url, self.token, container)
|
||||||
|
except client.ClientException, e:
|
||||||
|
self.assertEquals(e.http_status, 503)
|
||||||
|
else:
|
||||||
|
client.delete_container(self.url, self.token, container)
|
||||||
|
|
||||||
|
pool = eventlet.GreenPool()
|
||||||
|
try:
|
||||||
|
with eventlet.Timeout(15):
|
||||||
|
p = pool.spawn(run_test, 1, False)
|
||||||
|
r = pool.spawn(run_test, 2, True)
|
||||||
|
q = pool.spawn(run_test, 3, True)
|
||||||
|
pool.waitall()
|
||||||
|
except eventlet.Timeout, e:
|
||||||
|
raise Exception(
|
||||||
|
"The server did not return a 503 on container db locks, "
|
||||||
|
"it just hangs: %s" % e)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -161,8 +161,10 @@ def fake_http_connect(*code_iter, **kwargs):
|
|||||||
self.body = body
|
self.body = body
|
||||||
|
|
||||||
def getresponse(self):
|
def getresponse(self):
|
||||||
if 'raise_exc' in kwargs:
|
if kwargs.get('raise_exc'):
|
||||||
raise Exception('test')
|
raise Exception('test')
|
||||||
|
if kwargs.get('raise_timeout_exc'):
|
||||||
|
raise TimeoutError()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def getexpect(self):
|
def getexpect(self):
|
||||||
@@ -341,6 +343,14 @@ class TestController(unittest.TestCase):
|
|||||||
self.assertEqual(p, partition)
|
self.assertEqual(p, partition)
|
||||||
self.assertEqual(n, nodes)
|
self.assertEqual(n, nodes)
|
||||||
|
|
||||||
|
def test_make_requests(self):
|
||||||
|
with save_globals():
|
||||||
|
proxy_server.http_connect = fake_http_connect(200)
|
||||||
|
partition, nodes = self.controller.account_info(self.account)
|
||||||
|
proxy_server.http_connect = fake_http_connect(201,
|
||||||
|
raise_timeout_exc=True)
|
||||||
|
self.controller._make_request(nodes, partition, 'POST','/','','')
|
||||||
|
|
||||||
# tests if 200 is cached and used
|
# tests if 200 is cached and used
|
||||||
def test_account_info_200(self):
|
def test_account_info_200(self):
|
||||||
with save_globals():
|
with save_globals():
|
||||||
@@ -1893,8 +1903,8 @@ class TestObjectController(unittest.TestCase):
|
|||||||
_test_sockets
|
_test_sockets
|
||||||
orig_update_request = prosrv.update_request
|
orig_update_request = prosrv.update_request
|
||||||
|
|
||||||
def broken_update_request(env, req):
|
def broken_update_request(*args, **kwargs):
|
||||||
raise Exception('fake')
|
raise Exception('fake: this should be printed')
|
||||||
|
|
||||||
prosrv.update_request = broken_update_request
|
prosrv.update_request = broken_update_request
|
||||||
sock = connect_tcp(('localhost', prolis.getsockname()[1]))
|
sock = connect_tcp(('localhost', prolis.getsockname()[1]))
|
||||||
@@ -1925,6 +1935,35 @@ class TestObjectController(unittest.TestCase):
|
|||||||
self.assertEquals(headers[:len(exp)], exp)
|
self.assertEquals(headers[:len(exp)], exp)
|
||||||
self.assert_('\r\nContent-Length: 0\r\n' in headers)
|
self.assert_('\r\nContent-Length: 0\r\n' in headers)
|
||||||
|
|
||||||
|
def test_client_ip_logging(self):
|
||||||
|
# test that the client ip field in the log gets populated with the
|
||||||
|
# ip instead of being blank
|
||||||
|
(prosrv, acc1srv, acc2srv, con2srv, con2srv, obj1srv, obj2srv) = \
|
||||||
|
_test_servers
|
||||||
|
(prolis, acc1lis, acc2lis, con2lis, con2lis, obj1lis, obj2lis) = \
|
||||||
|
_test_sockets
|
||||||
|
|
||||||
|
class Logger(object):
|
||||||
|
|
||||||
|
def info(self, msg):
|
||||||
|
self.msg = msg
|
||||||
|
|
||||||
|
orig_logger, orig_access_logger = prosrv.logger, prosrv.access_logger
|
||||||
|
prosrv.logger = prosrv.access_logger = Logger()
|
||||||
|
sock = connect_tcp(('localhost', prolis.getsockname()[1]))
|
||||||
|
fd = sock.makefile()
|
||||||
|
fd.write(
|
||||||
|
'GET /v1/a?format=json HTTP/1.1\r\nHost: localhost\r\n'
|
||||||
|
'Connection: close\r\nX-Auth-Token: t\r\n'
|
||||||
|
'Content-Length: 0\r\n'
|
||||||
|
'\r\n')
|
||||||
|
fd.flush()
|
||||||
|
headers = readuntil2crlfs(fd)
|
||||||
|
exp = 'HTTP/1.1 200'
|
||||||
|
self.assertEquals(headers[:len(exp)], exp)
|
||||||
|
exp = '127.0.0.1 127.0.0.1'
|
||||||
|
self.assert_(exp in prosrv.logger.msg)
|
||||||
|
|
||||||
def test_chunked_put_logging(self):
|
def test_chunked_put_logging(self):
|
||||||
# GET account with a query string to test that
|
# GET account with a query string to test that
|
||||||
# Application.log_request logs the query string. Also, throws
|
# Application.log_request logs the query string. Also, throws
|
||||||
|
|||||||
Reference in New Issue
Block a user