From 6c13001244d715e1780771a71a11e13f0c62e610 Mon Sep 17 00:00:00 2001 From: gholt Date: Thu, 31 Mar 2011 22:32:41 +0000 Subject: [PATCH 1/8] Rename swift-stats-* to swift-dispersion-* to avoid confusion with log stats stuff --- bin/swift-dispersion-populate | 152 +++++++++++++++++++++ bin/swift-dispersion-report | 249 ++++++++++++++++++++++++++++++++++ bin/swift-stats-populate | 7 +- bin/swift-stats-report | 5 + doc/source/admin_guide.rst | 39 +++--- etc/dispersion.conf-sample | 8 ++ setup.py | 1 + 7 files changed, 437 insertions(+), 24 deletions(-) create mode 100755 bin/swift-dispersion-populate create mode 100755 bin/swift-dispersion-report create mode 100644 etc/dispersion.conf-sample diff --git a/bin/swift-dispersion-populate b/bin/swift-dispersion-populate new file mode 100755 index 0000000000..fe9fc56f05 --- /dev/null +++ b/bin/swift-dispersion-populate @@ -0,0 +1,152 @@ +#!/usr/bin/python -u +# Copyright (c) 2010-2011 OpenStack, LLC. +# +# 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. + +import os +import traceback +from ConfigParser import ConfigParser +from cStringIO import StringIO +from sys import exit, argv +from time import time +from uuid import uuid4 + +from eventlet import GreenPool, patcher, sleep +from eventlet.pools import Pool + +from swift.common.client import Connection, get_auth +from swift.common.ring import Ring +from swift.common.utils import compute_eta, get_time_units + + +def put_container(connpool, container, report): + global retries_done + try: + with connpool.item() as conn: + conn.put_container(container) + retries_done += conn.attempts - 1 + if report: + report(True) + except Exception: + if report: + report(False) + raise + + +def put_object(connpool, container, obj, report): + global retries_done + try: + with connpool.item() as conn: + conn.put_object(container, obj, StringIO(obj), + headers={'x-object-meta-dispersion': obj}) + retries_done += conn.attempts - 1 + if report: + report(True) + except Exception: + if report: + report(False) + raise + + +def report(success): + global begun, created, item_type, next_report, need_to_create, retries_done + if not success: + traceback.print_exc() + exit('Gave up due to error(s).') + created += 1 + if time() < next_report: + return + next_report = time() + 5 + eta, eta_unit = compute_eta(begun, created, need_to_create) + print '\r\x1B[KCreating %s: %d of %d, %d%s left, %d retries' % (item_type, + created, need_to_create, round(eta), eta_unit, retries_done), + + +if __name__ == '__main__': + global begun, created, item_type, next_report, need_to_create, retries_done + patcher.monkey_patch() + + conffile = '/etc/swift/dispersion.conf' + if len(argv) == 2: + conffile = argv[1] + elif len(argv) > 2: + exit('Syntax: %s [conffile]' % argv[0]) + c = ConfigParser() + if not c.read(conffile): + exit('Unable to read config file: %s' % conffile) + conf = dict(c.items('dispersion')) + swift_dir = conf.get('swift_dir', '/etc/swift') + dispersion_coverage = int(conf.get('dispersion_coverage', 1)) + retries = int(conf.get('retries', 5)) + concurrency = int(conf.get('concurrency', 25)) + + coropool = GreenPool(size=concurrency) + retries_done = 0 + + url, token = get_auth(conf['auth_url'], conf['auth_user'], + conf['auth_key']) + account = url.rsplit('/', 1)[1] + connpool = Pool(max_size=concurrency) + connpool.create = lambda: Connection(conf['auth_url'], + conf['auth_user'], conf['auth_key'], + retries=retries, + preauthurl=url, preauthtoken=token) + + container_ring = Ring(os.path.join(swift_dir, 'container.ring.gz')) + parts_left = dict((x, x) for x in xrange(container_ring.partition_count)) + item_type = 'containers' + created = 0 + retries_done = 0 + need_to_create = need_to_queue = \ + dispersion_coverage / 100.0 * container_ring.partition_count + begun = next_report = time() + next_report += 2 + while need_to_queue >= 1: + container = 'dispersion_%s' % uuid4().hex + part, _junk = container_ring.get_nodes(account, container) + if part in parts_left: + coropool.spawn(put_container, connpool, container, report) + sleep() + del parts_left[part] + need_to_queue -= 1 + coropool.waitall() + elapsed, elapsed_unit = get_time_units(time() - begun) + print '\r\x1B[KCreated %d containers for dispersion reporting, %d%s, %d ' \ + 'retries' % \ + (need_to_create, round(elapsed), elapsed_unit, retries_done) + + container = 'dispersion_objects' + put_container(connpool, container, None) + object_ring = Ring(os.path.join(swift_dir, 'object.ring.gz')) + parts_left = dict((x, x) for x in xrange(object_ring.partition_count)) + item_type = 'objects' + created = 0 + retries_done = 0 + need_to_create = need_to_queue = \ + dispersion_coverage / 100.0 * object_ring.partition_count + begun = next_report = time() + next_report += 2 + while need_to_queue >= 1: + obj = 'dispersion_%s' % uuid4().hex + part, _junk = object_ring.get_nodes(account, container, obj) + if part in parts_left: + coropool.spawn(put_object, connpool, container, obj, report) + sleep() + del parts_left[part] + need_to_queue -= 1 + coropool.waitall() + elapsed, elapsed_unit = get_time_units(time() - begun) + print '\r\x1B[KCreated %d objects for dispersion reporting, %d%s, %d ' \ + 'retries' % \ + (need_to_create, round(elapsed), elapsed_unit, retries_done) diff --git a/bin/swift-dispersion-report b/bin/swift-dispersion-report new file mode 100755 index 0000000000..2ec4c31615 --- /dev/null +++ b/bin/swift-dispersion-report @@ -0,0 +1,249 @@ +#!/usr/bin/python -u +# Copyright (c) 2010-2011 OpenStack, LLC. +# +# 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. + +import csv +import os +import socket +from ConfigParser import ConfigParser +from httplib import HTTPException +from optparse import OptionParser +from sys import argv, exit, stderr +from time import time +from uuid import uuid4 + +from eventlet import GreenPool, hubs, patcher, sleep, Timeout +from eventlet.pools import Pool + +from swift.common import direct_client +from swift.common.client import ClientException, Connection, get_auth +from swift.common.ring import Ring +from swift.common.utils import compute_eta, get_time_units + + +unmounted = [] + +def get_error_log(prefix): + def error_log(msg_or_exc): + global unmounted + if hasattr(msg_or_exc, 'http_status') and \ + msg_or_exc.http_status == 507: + identifier = '%s:%s/%s' + if identifier not in unmounted: + unmounted.append(identifier) + print >>stderr, 'ERROR: %s:%s/%s is unmounted -- This will ' \ + 'cause replicas designated for that device to be ' \ + 'considered missing until resolved or the ring is ' \ + 'updated.' % (msg_or_exc.http_host, msg_or_exc.http_port, + msg_or_exc.http_device) + if not hasattr(msg_or_exc, 'http_status') or \ + msg_or_exc.http_status not in (404, 507): + print >>stderr, 'ERROR: %s: %s' % (prefix, msg_or_exc) + return error_log + + +def container_dispersion_report(coropool, connpool, account, container_ring, + retries): + with connpool.item() as conn: + containers = [c['name'] for c in conn.get_account(prefix='dispersion_', + full_listing=True)[1]] + containers_listed = len(containers) + if not containers_listed: + print >>stderr, 'No containers to query. Has ' \ + 'swift-dispersion-populate been run?' + return + retries_done = [0] + containers_queried = [0] + container_copies_found = [0, 0, 0, 0] + begun = time() + next_report = [time() + 2] + def direct(container, part, nodes): + found_count = 0 + for node in nodes: + error_log = get_error_log('%(ip)s:%(port)s/%(device)s' % node) + try: + attempts, _junk = direct_client.retry( + direct_client.direct_head_container, node, + part, account, container, error_log=error_log, + retries=retries) + retries_done[0] += attempts - 1 + found_count += 1 + except ClientException, err: + if err.http_status not in (404, 507): + error_log('Giving up on /%s/%s/%s: %s' % (part, account, + container, err)) + except (Exception, Timeout), err: + error_log('Giving up on /%s/%s/%s: %s' % (part, account, + container, err)) + container_copies_found[found_count] += 1 + containers_queried[0] += 1 + if time() >= next_report[0]: + next_report[0] = time() + 5 + eta, eta_unit = compute_eta(begun, containers_queried[0], + containers_listed) + print '\r\x1B[KQuerying containers: %d of %d, %d%s left, %d ' \ + 'retries' % (containers_queried[0], containers_listed, + round(eta), eta_unit, retries_done[0]), + container_parts = {} + for container in containers: + part, nodes = container_ring.get_nodes(account, container) + if part not in container_parts: + container_parts[part] = part + coropool.spawn(direct, container, part, nodes) + coropool.waitall() + distinct_partitions = len(container_parts) + copies_expected = distinct_partitions * container_ring.replica_count + copies_found = sum(a * b for a, b in enumerate(container_copies_found)) + value = 100.0 * copies_found / copies_expected + elapsed, elapsed_unit = get_time_units(time() - begun) + print '\r\x1B[KQueried %d containers for dispersion reporting, ' \ + '%d%s, %d retries' % (containers_listed, round(elapsed), + elapsed_unit, retries_done[0]) + if containers_listed - distinct_partitions: + print 'There were %d overlapping partitions' % ( + containers_listed - distinct_partitions) + if container_copies_found[2]: + print 'There were %d partitions missing one copy.' % \ + container_copies_found[2] + if container_copies_found[1]: + print '! There were %d partitions missing two copies.' % \ + container_copies_found[1] + if container_copies_found[0]: + print '!!! There were %d partitions missing all copies.' % \ + container_copies_found[0] + print '%.02f%% of container copies found (%d of %d)' % ( + value, copies_found, copies_expected) + print 'Sample represents %.02f%% of the container partition space' % ( + 100.0 * distinct_partitions / container_ring.partition_count) + + +def object_dispersion_report(coropool, connpool, account, object_ring, + retries): + container = 'dispersion_objects' + with connpool.item() as conn: + try: + objects = [o['name'] for o in conn.get_container(container, + prefix='dispersion_', full_listing=True)[1]] + except ClientException, err: + if err.http_status != 404: + raise + print >>stderr, 'No objects to query. Has ' \ + 'swift-dispersion-populate been run?' + return + objects_listed = len(objects) + if not objects_listed: + print >>stderr, 'No objects to query. Has swift-dispersion-populate ' \ + 'been run?' + return + retries_done = [0] + objects_queried = [0] + object_copies_found = [0, 0, 0, 0] + begun = time() + next_report = [time() + 2] + def direct(obj, part, nodes): + found_count = 0 + for node in nodes: + error_log = get_error_log('%(ip)s:%(port)s/%(device)s' % node) + try: + attempts, _junk = direct_client.retry( + direct_client.direct_head_object, node, part, + account, container, obj, error_log=error_log, + retries=retries) + retries_done[0] += attempts - 1 + found_count += 1 + except ClientException, err: + if err.http_status not in (404, 507): + error_log('Giving up on /%s/%s/%s/%s: %s' % (part, account, + container, obj, err)) + except (Exception, Timeout), err: + error_log('Giving up on /%s/%s/%s/%s: %s' % (part, account, + container, obj, err)) + object_copies_found[found_count] += 1 + objects_queried[0] += 1 + if time() >= next_report[0]: + next_report[0] = time() + 5 + eta, eta_unit = compute_eta(begun, objects_queried[0], + objects_listed) + print '\r\x1B[KQuerying objects: %d of %d, %d%s left, %d ' \ + 'retries' % (objects_queried[0], objects_listed, round(eta), + eta_unit, retries_done[0]), + object_parts = {} + for obj in objects: + part, nodes = object_ring.get_nodes(account, container, obj) + if part not in object_parts: + object_parts[part] = part + coropool.spawn(direct, obj, part, nodes) + coropool.waitall() + distinct_partitions = len(object_parts) + copies_expected = distinct_partitions * object_ring.replica_count + copies_found = sum(a * b for a, b in enumerate(object_copies_found)) + value = 100.0 * copies_found / copies_expected + elapsed, elapsed_unit = get_time_units(time() - begun) + print '\r\x1B[KQueried %d objects for dispersion reporting, ' \ + '%d%s, %d retries' % (objects_listed, round(elapsed), + elapsed_unit, retries_done[0]) + if objects_listed - distinct_partitions: + print 'There were %d overlapping partitions' % ( + objects_listed - distinct_partitions) + if object_copies_found[2]: + print 'There were %d partitions missing one copy.' % \ + object_copies_found[2] + if object_copies_found[1]: + print '! There were %d partitions missing two copies.' % \ + object_copies_found[1] + if object_copies_found[0]: + print '!!! There were %d partitions missing all copies.' % \ + object_copies_found[0] + print '%.02f%% of object copies found (%d of %d)' % \ + (value, copies_found, copies_expected) + print 'Sample represents %.02f%% of the object partition space' % ( + 100.0 * distinct_partitions / object_ring.partition_count) + + +if __name__ == '__main__': + patcher.monkey_patch() + hubs.get_hub().debug_exceptions = False + + conffile = '/etc/swift/dispersion.conf' + if len(argv) == 2: + conffile = argv[1] + elif len(argv) > 2: + exit('Syntax: %s [conffile]' % argv[0]) + c = ConfigParser() + if not c.read(conffile): + exit('Unable to read config file: %s' % conffile) + conf = dict(c.items('dispersion')) + swift_dir = conf.get('swift_dir', '/etc/swift') + dispersion_coverage = int(conf.get('dispersion_coverage', 1)) + retries = int(conf.get('retries', 5)) + concurrency = int(conf.get('concurrency', 25)) + + coropool = GreenPool(size=concurrency) + + url, token = get_auth(conf['auth_url'], conf['auth_user'], + conf['auth_key']) + account = url.rsplit('/', 1)[1] + connpool = Pool(max_size=concurrency) + connpool.create = lambda: Connection(conf['auth_url'], + conf['auth_user'], conf['auth_key'], + retries=retries, + preauthurl=url, preauthtoken=token) + + container_ring = Ring(os.path.join(swift_dir, 'container.ring.gz')) + object_ring = Ring(os.path.join(swift_dir, 'object.ring.gz')) + + container_dispersion_report(coropool, connpool, account, container_ring, + retries) + object_dispersion_report(coropool, connpool, account, object_ring, retries) diff --git a/bin/swift-stats-populate b/bin/swift-stats-populate index b1f4f0a568..080f159f85 100755 --- a/bin/swift-stats-populate +++ b/bin/swift-stats-populate @@ -18,7 +18,7 @@ import os import traceback from ConfigParser import ConfigParser from optparse import OptionParser -from sys import exit, argv +from sys import exit, argv, stderr from time import time from uuid import uuid4 @@ -77,6 +77,11 @@ if __name__ == '__main__': global begun, created, item_type, next_report, need_to_create, retries_done patcher.monkey_patch() + print >>stderr, ''' +WARNING: This command is being replaced with swift-dispersion-populate; you +should switch to that before the next Swift release. + ''' + parser = OptionParser() parser.add_option('-d', '--dispersion', action='store_true', dest='dispersion', default=False, diff --git a/bin/swift-stats-report b/bin/swift-stats-report index 4c47b404de..e9328a1359 100755 --- a/bin/swift-stats-report +++ b/bin/swift-stats-report @@ -749,6 +749,11 @@ if __name__ == '__main__': patcher.monkey_patch() hubs.get_hub().debug_exceptions = False + print >>stderr, ''' +WARNING: This command is being replaced with swift-dispersion-report; you +should switch to that before the next Swift release. + ''' + parser = OptionParser(usage=''' Usage: %prog [options] [conf_file] diff --git a/doc/source/admin_guide.rst b/doc/source/admin_guide.rst index bb3eef6fa6..ab112bbb38 100644 --- a/doc/source/admin_guide.rst +++ b/doc/source/admin_guide.rst @@ -134,9 +134,9 @@ different distro or OS, some care should be taken before using in production. Cluster Health -------------- -There is a swift-stats-report tool for measuring overall cluster health. This -is accomplished by checking if a set of deliberately distributed containers and -objects are currently in their proper places within the cluster. +There is a swift-dispersion-report tool for measuring overall cluster health. +This is accomplished by checking if a set of deliberately distributed +containers and objects are currently in their proper places within the cluster. For instance, a common deployment has three replicas of each object. The health of that object can be measured by checking if each replica is in its proper @@ -153,15 +153,15 @@ to gather results. The first thing that needs to be done to provide this health value is create a new account solely for this usage. Next, we need to place the containers and objects throughout the system so that they are on distinct partitions. The -swift-stats-populate tool does this by making up random container and object -names until they fall on distinct partitions. Last, and repeatedly for the life -of the cluster, we need to run the swift-stats-report tool to check the health -of each of these containers and objects. +swift-dispersion-populate tool does this by making up random container and +object names until they fall on distinct partitions. Last, and repeatedly for +the life of the cluster, we need to run the swift-dispersion-report tool to +check the health of each of these containers and objects. These tools need direct access to the entire cluster and to the ring files (installing them on a proxy server will probably do). Both -swift-stats-populate and swift-stats-report use the same configuration file, -/etc/swift/stats.conf. Example conf file:: +swift-dispersion-populate and swift-dispersion-report use the same +configuration file, /etc/swift/dispersion.conf. Example conf file:: [stats] auth_url = http://saio:11000/auth/v1.0 @@ -169,17 +169,17 @@ swift-stats-populate and swift-stats-report use the same configuration file, auth_key = testing There are also options for the conf file for specifying the dispersion coverage -(defaults to 1%), retries, concurrency, CSV output file, etc. though usually -the defaults are fine. +(defaults to 1%), retries, concurrency, etc. though usually the defaults are +fine. -Once the configuration is in place, run `swift-stats-populate -d` to populate +Once the configuration is in place, run `swift-dispersion-populate` to populate the containers and objects throughout the cluster. Now that those containers and objects are in place, you can run -`swift-stats-report -d` to get a dispersion report, or the overall health of +`swift-dispersion-report` to get a dispersion report, or the overall health of the cluster. Here is an example of a cluster in perfect health:: - $ swift-stats-report -d + $ swift-dispersion-report Queried 2621 containers for dispersion reporting, 19s, 0 retries 100.00% of container copies found (7863 of 7863) Sample represents 1.00% of the container partition space @@ -195,7 +195,7 @@ that has:: $ swift-ring-builder object.builder set_weight d0 200 $ swift-ring-builder object.builder rebalance ... - $ swift-stats-report -d + $ swift-dispersion-report Queried 2621 containers for dispersion reporting, 8s, 0 retries 100.00% of container copies found (7863 of 7863) Sample represents 1.00% of the container partition space @@ -212,7 +212,7 @@ is much less. Next, I'll run the replicators to get everything put back into place and then rerun the dispersion report:: ... start object replicators and monitor logs until they're caught up ... - $ swift-stats-report -d + $ swift-dispersion-report Queried 2621 containers for dispersion reporting, 17s, 0 retries 100.00% of container copies found (7863 of 7863) Sample represents 1.00% of the container partition space @@ -221,13 +221,6 @@ place and then rerun the dispersion report:: 100.00% of object copies found (7857 of 7857) Sample represents 1.00% of the object partition space -So that's a summation of how to use swift-stats-report to monitor the health of -a cluster. There are a few other things it can do, such as performance -monitoring, but those are currently in their infancy and little used. For -instance, you can run `swift-stats-populate -p` and `swift-stats-report -p` to -get performance timings (warning: the initial populate takes a while). These -timings are dumped into a CSV file (/etc/swift/stats.csv by default) and can -then be graphed to see how cluster performance is trending. ------------------------------------ Additional Cleanup Script for Swauth diff --git a/etc/dispersion.conf-sample b/etc/dispersion.conf-sample new file mode 100644 index 0000000000..09c4290e7d --- /dev/null +++ b/etc/dispersion.conf-sample @@ -0,0 +1,8 @@ +[dispersion] +auth_url = http://saio:8080/auth/v1.0 +auth_user = test:tester +auth_key = testing +# swift_dir = /etc/swift +# dispersion_coverage = 1 +# retries = 5 +# concurrency = 25 diff --git a/setup.py b/setup.py index ccd1d4dd95..93d13a16af 100644 --- a/setup.py +++ b/setup.py @@ -90,6 +90,7 @@ setup( 'bin/swift-object-updater', 'bin/swift-proxy-server', 'bin/swift-ring-builder', 'bin/swift-stats-populate', 'bin/swift-stats-report', + 'bin/swift-dispersion-populate', 'bin/swift-dispersion-report', 'bin/swift-bench', 'bin/swift-log-uploader', 'bin/swift-log-stats-collector', From 26b5ff12bf55e745a07468ea66b85f589786b07a Mon Sep 17 00:00:00 2001 From: John Dickinson Date: Fri, 6 May 2011 09:50:06 -0500 Subject: [PATCH 2/8] added transaction id header to every response --- swift/proxy/server.py | 9 +++++--- test/unit/proxy/test_server.py | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 99b7201ab4..1c9c2a88f5 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -1448,18 +1448,21 @@ class BaseApplication(object): if self.memcache is None: self.memcache = cache_from_env(env) req = self.update_request(Request(env)) + trans_id = req.headers.get('x-cf-trans-id') if 'eventlet.posthooks' in env: env['eventlet.posthooks'].append( (self.posthooklogger, (req,), {})) - return self.handle_request(req)(env, start_response) + resp = self.handle_request(req) else: # Lack of posthook support means that we have to log on the # start of the response, rather than after all the data has # been sent. This prevents logging client disconnects # differently than full transmissions. - response = self.handle_request(req)(env, start_response) + resp = self.handle_request(req) self.posthooklogger(env, req) - return response + if trans_id: + resp.headers['x-trans-id'] = trans_id + return resp(env, start_response) except Exception: print "EXCEPTION IN __call__: %s: %s" % \ (traceback.format_exc(), env) diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index a254ac1ad6..272aaa7c4f 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -656,6 +656,46 @@ class TestProxyServer(unittest.TestCase): resp = app.handle_request(req) self.assert_(called[0]) + def test_trans_id_header(self): + + def start_response(status, headers): + self.assert_('x-trans-id' in (x[0] for x in headers)) + with save_globals(): + proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) + app = proxy_server.Application(None, FakeMemcache(), + account_ring=FakeRing(), container_ring=FakeRing(), + object_ring=FakeRing()) + req = Request.blank('/v1/a') + app(req.environ, start_response) + + proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) + app = proxy_server.Application(None, FakeMemcache(), + account_ring=FakeRing(), container_ring=FakeRing(), + object_ring=FakeRing()) + req = Request.blank('/v1/a/c') + app(req.environ, start_response) + + proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) + app = proxy_server.Application(None, FakeMemcache(), + account_ring=FakeRing(), container_ring=FakeRing(), + object_ring=FakeRing()) + req = Request.blank('/v1/a/c/o') + app(req.environ, start_response) + + proxy_server.http_connect = fake_http_connect(200, 404, 404, 404) + app = proxy_server.Application(None, FakeMemcache(), + account_ring=FakeRing(), container_ring=FakeRing(), + object_ring=FakeRing()) + req = Request.blank('/v1/a/c/o') + app(req.environ, start_response) + + proxy_server.http_connect = fake_http_connect(200, 503, 503, 503) + app = proxy_server.Application(None, FakeMemcache(), + account_ring=FakeRing(), container_ring=FakeRing(), + object_ring=FakeRing()) + req = Request.blank('/v1/a/c/o') + app(req.environ, start_response) + class TestObjectController(unittest.TestCase): From 4d3c76ab851148e85be8cd5f48120eabd957ecab Mon Sep 17 00:00:00 2001 From: John Dickinson Date: Mon, 9 May 2011 15:21:34 -0500 Subject: [PATCH 3/8] moved transaction id to catch errors middleware --- swift/account/server.py | 6 ++-- swift/common/middleware/catch_errors.py | 13 ++++++- swift/common/middleware/swauth.py | 2 +- swift/container/server.py | 6 ++-- swift/obj/server.py | 8 ++--- swift/proxy/server.py | 30 ++++++++-------- test/unit/common/middleware/test_except.py | 23 ++++++++++++ test/unit/proxy/test_server.py | 42 +--------------------- 8 files changed, 61 insertions(+), 69 deletions(-) diff --git a/swift/account/server.py b/swift/account/server.py index 54f13177f5..43c9ade2be 100644 --- a/swift/account/server.py +++ b/swift/account/server.py @@ -86,7 +86,7 @@ class AccountController(object): return Response(status='507 %s is not mounted' % drive) broker = self._get_account_broker(drive, part, account) if container: # put account container - if 'x-cf-trans-id' in req.headers: + if 'x-trans-id' in req.headers: broker.pending_timeout = 3 if req.headers.get('x-account-override-deleted', 'no').lower() != \ 'yes' and broker.is_deleted(): @@ -296,7 +296,7 @@ class AccountController(object): def __call__(self, env, start_response): start_time = time.time() req = Request(env) - self.logger.txn_id = req.headers.get('x-cf-trans-id', None) + self.logger.txn_id = req.headers.get('x-trans-id', None) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: @@ -319,7 +319,7 @@ class AccountController(object): time.strftime('%d/%b/%Y:%H:%M:%S +0000', time.gmtime()), req.method, req.path, res.status.split()[0], res.content_length or '-', - req.headers.get('x-cf-trans-id', '-'), + req.headers.get('x-trans-id', '-'), req.referer or '-', req.user_agent or '-', trans_time, additional_info) diff --git a/swift/common/middleware/catch_errors.py b/swift/common/middleware/catch_errors.py index 716bda4da1..8f6802d2a1 100644 --- a/swift/common/middleware/catch_errors.py +++ b/swift/common/middleware/catch_errors.py @@ -15,6 +15,7 @@ from webob import Request from webob.exc import HTTPServerError +import uuid from swift.common.utils import get_logger @@ -29,13 +30,23 @@ class CatchErrorMiddleware(object): self.logger = get_logger(conf, log_route='catch-errors') def __call__(self, env, start_response): + trans_id = env.get('HTTP_X_TRANS_ID') + if not trans_id: + trans_id = uuid.uuid4().hex + env['HTTP_X_TRANS_ID'] = 'tx' + trans_id try: - return self.app(env, start_response) + + def my_start_response(status, response_headers, exc_info=None): + trans_header = ('x-trans-id', trans_id) + response_headers.append(trans_header) + return start_response(status, response_headers, exc_info) + return self.app(env, my_start_response) except Exception, err: self.logger.exception(_('Error: %s'), err) resp = HTTPServerError(request=Request(env), body='An error occurred', content_type='text/plain') + resp.headers['x-trans-id'] = trans_id return resp(env, start_response) diff --git a/swift/common/middleware/swauth.py b/swift/common/middleware/swauth.py index 328799a19a..5f51fb40bd 100644 --- a/swift/common/middleware/swauth.py +++ b/swift/common/middleware/swauth.py @@ -1360,7 +1360,7 @@ class Swauth(object): getattr(req, 'bytes_transferred', 0) or '-', getattr(response, 'bytes_transferred', 0) or '-', req.headers.get('etag', '-'), - req.headers.get('x-cf-trans-id', '-'), logged_headers or '-', + req.headers.get('x-trans-id', '-'), logged_headers or '-', trans_time))) diff --git a/swift/container/server.py b/swift/container/server.py index 3ced13d61f..01173c852f 100644 --- a/swift/container/server.py +++ b/swift/container/server.py @@ -96,7 +96,7 @@ class ContainerController(object): 'x-delete-timestamp': info['delete_timestamp'], 'x-object-count': info['object_count'], 'x-bytes-used': info['bytes_used'], - 'x-cf-trans-id': req.headers.get('X-Cf-Trans-Id', '-')} + 'x-trans-id': req.headers.get('x-trans-id', '-')} if req.headers.get('x-account-override-deleted', 'no').lower() == \ 'yes': account_headers['x-account-override-deleted'] = 'yes' @@ -385,7 +385,7 @@ class ContainerController(object): def __call__(self, env, start_response): start_time = time.time() req = Request(env) - self.logger.txn_id = req.headers.get('x-cf-trans-id', None) + self.logger.txn_id = req.headers.get('x-trans-id', None) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: @@ -405,7 +405,7 @@ class ContainerController(object): time.gmtime()), req.method, req.path, res.status.split()[0], res.content_length or '-', - req.headers.get('x-cf-trans-id', '-'), + req.headers.get('x-trans-id', '-'), req.referer or '-', req.user_agent or '-', trans_time) if req.method.upper() == 'REPLICATE': diff --git a/swift/obj/server.py b/swift/obj/server.py index 6e67dc0ff2..75766d112e 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -549,7 +549,7 @@ class ObjectController(object): 'x-content-type': file.metadata['Content-Type'], 'x-timestamp': file.metadata['X-Timestamp'], 'x-etag': file.metadata['ETag'], - 'x-cf-trans-id': request.headers.get('x-cf-trans-id', '-')}, + 'x-trans-id': request.headers.get('x-trans-id', '-')}, device) resp = HTTPCreated(request=request, etag=etag) return resp @@ -686,7 +686,7 @@ class ObjectController(object): file.unlinkold(metadata['X-Timestamp']) self.container_update('DELETE', account, container, obj, request.headers, {'x-timestamp': metadata['X-Timestamp'], - 'x-cf-trans-id': request.headers.get('x-cf-trans-id', '-')}, + 'x-trans-id': request.headers.get('x-trans-id', '-')}, device) resp = response_class(request=request) return resp @@ -719,7 +719,7 @@ class ObjectController(object): """WSGI Application entry point for the Swift Object Server.""" start_time = time.time() req = Request(env) - self.logger.txn_id = req.headers.get('x-cf-trans-id', None) + self.logger.txn_id = req.headers.get('x-trans-id', None) if not check_utf8(req.path_info): res = HTTPPreconditionFailed(body='Invalid UTF8') else: @@ -740,7 +740,7 @@ class ObjectController(object): time.gmtime()), req.method, req.path, res.status.split()[0], res.content_length or '-', req.referer or '-', - req.headers.get('x-cf-trans-id', '-'), + req.headers.get('x-trans-id', '-'), req.user_agent or '-', trans_time) if req.method == 'REPLICATE': diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 1c9c2a88f5..75753ac26a 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -358,7 +358,7 @@ class Controller(object): result_code = 0 attempts_left = self.app.account_ring.replica_count path = '/%s' % account - headers = {'x-cf-trans-id': self.trans_id} + headers = {'x-trans-id': self.trans_id} for node in self.iter_nodes(partition, nodes, self.app.account_ring): try: with ConnectionTimeout(self.app.conn_timeout): @@ -430,7 +430,7 @@ class Controller(object): write_acl = None container_size = None attempts_left = self.app.container_ring.replica_count - headers = {'x-cf-trans-id': self.trans_id} + headers = {'x-trans-id': self.trans_id} for node in self.iter_nodes(partition, nodes, self.app.container_ring): try: with ConnectionTimeout(self.app.conn_timeout): @@ -1227,7 +1227,7 @@ class ContainerController(Controller): headers = [] for account in accounts: nheaders = {'X-Timestamp': normalize_timestamp(time.time()), - 'x-cf-trans-id': self.trans_id, + 'x-trans-id': self.trans_id, 'X-Account-Host': '%(ip)s:%(port)s' % account, 'X-Account-Partition': account_partition, 'X-Account-Device': account['device']} @@ -1255,7 +1255,7 @@ class ContainerController(Controller): container_partition, containers = self.app.container_ring.get_nodes( self.account_name, self.container_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), - 'x-cf-trans-id': self.trans_id} + 'x-trans-id': self.trans_id} headers.update(value for value in req.headers.iteritems() if value[0].lower() in self.pass_through_headers or value[0].lower().startswith('x-container-meta-')) @@ -1278,7 +1278,7 @@ class ContainerController(Controller): headers = [] for account in accounts: headers.append({'X-Timestamp': normalize_timestamp(time.time()), - 'X-Cf-Trans-Id': self.trans_id, + 'X-Trans-Id': self.trans_id, 'X-Account-Host': '%(ip)s:%(port)s' % account, 'X-Account-Partition': account_partition, 'X-Account-Device': account['device']}) @@ -1323,7 +1323,7 @@ class AccountController(Controller): account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), - 'x-cf-trans-id': self.trans_id} + 'x-trans-id': self.trans_id} headers.update(value for value in req.headers.iteritems() if value[0].lower().startswith('x-account-meta-')) if self.app.memcache: @@ -1340,7 +1340,7 @@ class AccountController(Controller): account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), - 'X-CF-Trans-Id': self.trans_id} + 'X-Trans-Id': self.trans_id} headers.update(value for value in req.headers.iteritems() if value[0].lower().startswith('x-account-meta-')) if self.app.memcache: @@ -1357,7 +1357,7 @@ class AccountController(Controller): account_partition, accounts = \ self.app.account_ring.get_nodes(self.account_name) headers = {'X-Timestamp': normalize_timestamp(time.time()), - 'X-CF-Trans-Id': self.trans_id} + 'X-Trans-Id': self.trans_id} if self.app.memcache: self.app.memcache.delete('account%s' % req.path_info.rstrip('/')) return self.make_requests(req, self.app.account_ring, @@ -1448,7 +1448,7 @@ class BaseApplication(object): if self.memcache is None: self.memcache = cache_from_env(env) req = self.update_request(Request(env)) - trans_id = req.headers.get('x-cf-trans-id') + trans_id = req.headers.get('x-trans-id') if 'eventlet.posthooks' in env: env['eventlet.posthooks'].append( (self.posthooklogger, (req,), {})) @@ -1460,8 +1460,8 @@ class BaseApplication(object): # differently than full transmissions. resp = self.handle_request(req) self.posthooklogger(env, req) - if trans_id: - resp.headers['x-trans-id'] = trans_id + #if trans_id: + # resp.headers['x-trans-id'] = trans_id return resp(env, start_response) except Exception: print "EXCEPTION IN __call__: %s: %s" % \ @@ -1476,8 +1476,6 @@ class BaseApplication(object): def update_request(self, req): req.bytes_transferred = '-' req.client_disconnect = False - if 'x-cf-trans-id' not in req.headers: - req.headers['x-cf-trans-id'] = 'tx' + str(uuid.uuid4()) if 'x-storage-token' in req.headers and \ 'x-auth-token' not in req.headers: req.headers['x-auth-token'] = req.headers['x-storage-token'] @@ -1501,8 +1499,8 @@ class BaseApplication(object): return HTTPPreconditionFailed(request=req, body='Bad URL') controller = controller(self, **path_parts) - controller.trans_id = req.headers.get('x-cf-trans-id', '-') - self.logger.txn_id = req.headers.get('x-cf-trans-id', None) + controller.trans_id = req.headers.get('x-trans-id', '-') + self.logger.txn_id = req.headers.get('x-trans-id', None) try: handler = getattr(controller, req.method) if not getattr(handler, 'publicly_accessible'): @@ -1582,7 +1580,7 @@ class Application(BaseApplication): getattr(req, 'bytes_transferred', 0) or '-', getattr(response, 'bytes_transferred', 0) or '-', req.headers.get('etag', '-'), - req.headers.get('x-cf-trans-id', '-'), + req.headers.get('x-trans-id', '-'), logged_headers or '-', trans_time, ))) diff --git a/test/unit/common/middleware/test_except.py b/test/unit/common/middleware/test_except.py index 89c6e9ea1f..b6e18960ba 100644 --- a/test/unit/common/middleware/test_except.py +++ b/test/unit/common/middleware/test_except.py @@ -45,5 +45,28 @@ class TestCatchErrors(unittest.TestCase): resp = app(req.environ, start_response) self.assertEquals(resp, ['An error occurred']) + def test_trans_id_header(self): + + def start_response(status, headers): + self.assert_('x-trans-id' in (x[0] for x in headers)) + app = catch_errors.CatchErrorMiddleware(FakeApp(), {}) + req = Request.blank('/v1/a') + app(req.environ, start_response) + app = catch_errors.CatchErrorMiddleware(FakeApp(), {}) + req = Request.blank('/v1/a/c') + app(req.environ, start_response) + app = catch_errors.CatchErrorMiddleware(FakeApp(), {}) + req = Request.blank('/v1/a/c/o') + app(req.environ, start_response) + app = catch_errors.CatchErrorMiddleware(FakeApp(True), {}) + req = Request.blank('/v1/a') + app(req.environ, start_response) + app = catch_errors.CatchErrorMiddleware(FakeApp(True), {}) + req = Request.blank('/v1/a/c') + app(req.environ, start_response) + app = catch_errors.CatchErrorMiddleware(FakeApp(True), {}) + req = Request.blank('/v1/a/c/o') + app(req.environ, start_response) + if __name__ == '__main__': unittest.main() diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index 272aaa7c4f..fe2d1ca01e 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -124,7 +124,7 @@ def setup(): for node in nodes: conn = proxy_server.http_connect(node['ip'], node['port'], node['device'], partition, 'PUT', '/a', - {'X-Timestamp': ts, 'X-CF-Trans-Id': 'test'}) + {'X-Timestamp': ts, 'x-trans-id': 'test'}) resp = conn.getresponse() assert(resp.status == 201) # Create container @@ -656,46 +656,6 @@ class TestProxyServer(unittest.TestCase): resp = app.handle_request(req) self.assert_(called[0]) - def test_trans_id_header(self): - - def start_response(status, headers): - self.assert_('x-trans-id' in (x[0] for x in headers)) - with save_globals(): - proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) - app = proxy_server.Application(None, FakeMemcache(), - account_ring=FakeRing(), container_ring=FakeRing(), - object_ring=FakeRing()) - req = Request.blank('/v1/a') - app(req.environ, start_response) - - proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) - app = proxy_server.Application(None, FakeMemcache(), - account_ring=FakeRing(), container_ring=FakeRing(), - object_ring=FakeRing()) - req = Request.blank('/v1/a/c') - app(req.environ, start_response) - - proxy_server.http_connect = fake_http_connect(200, 200, 200, 200) - app = proxy_server.Application(None, FakeMemcache(), - account_ring=FakeRing(), container_ring=FakeRing(), - object_ring=FakeRing()) - req = Request.blank('/v1/a/c/o') - app(req.environ, start_response) - - proxy_server.http_connect = fake_http_connect(200, 404, 404, 404) - app = proxy_server.Application(None, FakeMemcache(), - account_ring=FakeRing(), container_ring=FakeRing(), - object_ring=FakeRing()) - req = Request.blank('/v1/a/c/o') - app(req.environ, start_response) - - proxy_server.http_connect = fake_http_connect(200, 503, 503, 503) - app = proxy_server.Application(None, FakeMemcache(), - account_ring=FakeRing(), container_ring=FakeRing(), - object_ring=FakeRing()) - req = Request.blank('/v1/a/c/o') - app(req.environ, start_response) - class TestObjectController(unittest.TestCase): From d0d98ba96ed2ad171ab790fd0c2058ed3056804b Mon Sep 17 00:00:00 2001 From: gholt Date: Thu, 12 May 2011 00:14:02 +0000 Subject: [PATCH 4/8] Updated to use standard shebang and explicitly flush output; added deprecation warning to stats.conf --- CHANGELOG | 3 ++- bin/swift-dispersion-populate | 7 +++++-- bin/swift-dispersion-report | 13 +++++++++++-- etc/stats.conf-sample | 3 +++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 97fe05d552..a85cbe3d65 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,5 @@ swift (x.x.x) * Renamed swift-stats-populate to swift-dispersion-populate and - swift-stats-report to swift-dispersion-report. + swift-stats-report to swift-dispersion-report with extraneous unused + options removed. The new tools use dispersion.conf instead of stats.conf. diff --git a/bin/swift-dispersion-populate b/bin/swift-dispersion-populate index fe9fc56f05..c5df32f741 100755 --- a/bin/swift-dispersion-populate +++ b/bin/swift-dispersion-populate @@ -1,4 +1,4 @@ -#!/usr/bin/python -u +#!/usr/bin/env python # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,7 +18,7 @@ import os import traceback from ConfigParser import ConfigParser from cStringIO import StringIO -from sys import exit, argv +from sys import exit, argv, stdout from time import time from uuid import uuid4 @@ -71,6 +71,7 @@ def report(success): eta, eta_unit = compute_eta(begun, created, need_to_create) print '\r\x1B[KCreating %s: %d of %d, %d%s left, %d retries' % (item_type, created, need_to_create, round(eta), eta_unit, retries_done), + stdout.flush() if __name__ == '__main__': @@ -125,6 +126,7 @@ if __name__ == '__main__': print '\r\x1B[KCreated %d containers for dispersion reporting, %d%s, %d ' \ 'retries' % \ (need_to_create, round(elapsed), elapsed_unit, retries_done) + stdout.flush() container = 'dispersion_objects' put_container(connpool, container, None) @@ -150,3 +152,4 @@ if __name__ == '__main__': print '\r\x1B[KCreated %d objects for dispersion reporting, %d%s, %d ' \ 'retries' % \ (need_to_create, round(elapsed), elapsed_unit, retries_done) + stdout.flush() diff --git a/bin/swift-dispersion-report b/bin/swift-dispersion-report index 2ec4c31615..9d0adb93fd 100755 --- a/bin/swift-dispersion-report +++ b/bin/swift-dispersion-report @@ -1,4 +1,4 @@ -#!/usr/bin/python -u +#!/usr/bin/env python # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,7 @@ import socket from ConfigParser import ConfigParser from httplib import HTTPException from optparse import OptionParser -from sys import argv, exit, stderr +from sys import argv, exit, stdout, stderr from time import time from uuid import uuid4 @@ -48,9 +48,11 @@ def get_error_log(prefix): 'considered missing until resolved or the ring is ' \ 'updated.' % (msg_or_exc.http_host, msg_or_exc.http_port, msg_or_exc.http_device) + stderr.flush() if not hasattr(msg_or_exc, 'http_status') or \ msg_or_exc.http_status not in (404, 507): print >>stderr, 'ERROR: %s: %s' % (prefix, msg_or_exc) + stderr.flush() return error_log @@ -63,6 +65,7 @@ def container_dispersion_report(coropool, connpool, account, container_ring, if not containers_listed: print >>stderr, 'No containers to query. Has ' \ 'swift-dispersion-populate been run?' + stderr.flush() return retries_done = [0] containers_queried = [0] @@ -96,6 +99,7 @@ def container_dispersion_report(coropool, connpool, account, container_ring, print '\r\x1B[KQuerying containers: %d of %d, %d%s left, %d ' \ 'retries' % (containers_queried[0], containers_listed, round(eta), eta_unit, retries_done[0]), + stdout.flush() container_parts = {} for container in containers: part, nodes = container_ring.get_nodes(account, container) @@ -127,6 +131,7 @@ def container_dispersion_report(coropool, connpool, account, container_ring, value, copies_found, copies_expected) print 'Sample represents %.02f%% of the container partition space' % ( 100.0 * distinct_partitions / container_ring.partition_count) + stdout.flush() def object_dispersion_report(coropool, connpool, account, object_ring, @@ -141,11 +146,13 @@ def object_dispersion_report(coropool, connpool, account, object_ring, raise print >>stderr, 'No objects to query. Has ' \ 'swift-dispersion-populate been run?' + stderr.flush() return objects_listed = len(objects) if not objects_listed: print >>stderr, 'No objects to query. Has swift-dispersion-populate ' \ 'been run?' + stderr.flush() return retries_done = [0] objects_queried = [0] @@ -179,6 +186,7 @@ def object_dispersion_report(coropool, connpool, account, object_ring, print '\r\x1B[KQuerying objects: %d of %d, %d%s left, %d ' \ 'retries' % (objects_queried[0], objects_listed, round(eta), eta_unit, retries_done[0]), + stdout.flush() object_parts = {} for obj in objects: part, nodes = object_ring.get_nodes(account, container, obj) @@ -210,6 +218,7 @@ def object_dispersion_report(coropool, connpool, account, object_ring, (value, copies_found, copies_expected) print 'Sample represents %.02f%% of the object partition space' % ( 100.0 * distinct_partitions / object_ring.partition_count) + stdout.flush() if __name__ == '__main__': diff --git a/etc/stats.conf-sample b/etc/stats.conf-sample index e9bceadb1f..f39fa5d541 100644 --- a/etc/stats.conf-sample +++ b/etc/stats.conf-sample @@ -1,3 +1,6 @@ +# WARNING: The swift-stats-populate and swift-stats-report commands are being +# replaced with swift-dispersion-populate and swift-dispersion-report; you +# should switch to those before the next Swift release. [stats] auth_url = http://saio:8080/auth/v1.0 auth_user = test:tester From 1d866acdb1941e679964f51c1dedf2981b3fb025 Mon Sep 17 00:00:00 2001 From: gholt Date: Thu, 12 May 2011 01:10:16 +0000 Subject: [PATCH 5/8] PEP8 fixes --- bin/swift-dispersion-report | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/swift-dispersion-report b/bin/swift-dispersion-report index 9d0adb93fd..dab174c46a 100755 --- a/bin/swift-dispersion-report +++ b/bin/swift-dispersion-report @@ -35,6 +35,7 @@ from swift.common.utils import compute_eta, get_time_units unmounted = [] + def get_error_log(prefix): def error_log(msg_or_exc): global unmounted @@ -72,6 +73,7 @@ def container_dispersion_report(coropool, connpool, account, container_ring, container_copies_found = [0, 0, 0, 0] begun = time() next_report = [time() + 2] + def direct(container, part, nodes): found_count = 0 for node in nodes: @@ -159,6 +161,7 @@ def object_dispersion_report(coropool, connpool, account, object_ring, object_copies_found = [0, 0, 0, 0] begun = time() next_report = [time() + 2] + def direct(obj, part, nodes): found_count = 0 for node in nodes: From 119ad8cff56de42ef44c298d6b41a4dcd39e4e6e Mon Sep 17 00:00:00 2001 From: John Dickinson Date: Thu, 12 May 2011 10:48:46 -0500 Subject: [PATCH 6/8] fixed error with setting the tx trans_id prefix --- swift/common/middleware/catch_errors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swift/common/middleware/catch_errors.py b/swift/common/middleware/catch_errors.py index 8f6802d2a1..9998a18723 100644 --- a/swift/common/middleware/catch_errors.py +++ b/swift/common/middleware/catch_errors.py @@ -32,8 +32,8 @@ class CatchErrorMiddleware(object): def __call__(self, env, start_response): trans_id = env.get('HTTP_X_TRANS_ID') if not trans_id: - trans_id = uuid.uuid4().hex - env['HTTP_X_TRANS_ID'] = 'tx' + trans_id + trans_id = 'tx' + uuid.uuid4().hex + env['HTTP_X_TRANS_ID'] = trans_id try: def my_start_response(status, response_headers, exc_info=None): From 990700aec1e1b0520cf05b75740474c99d166763 Mon Sep 17 00:00:00 2001 From: John Dickinson Date: Thu, 12 May 2011 10:57:35 -0500 Subject: [PATCH 7/8] reverted unneeded changes in proxy server --- swift/proxy/server.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/swift/proxy/server.py b/swift/proxy/server.py index 75753ac26a..3300e7a384 100644 --- a/swift/proxy/server.py +++ b/swift/proxy/server.py @@ -1448,21 +1448,18 @@ class BaseApplication(object): if self.memcache is None: self.memcache = cache_from_env(env) req = self.update_request(Request(env)) - trans_id = req.headers.get('x-trans-id') if 'eventlet.posthooks' in env: env['eventlet.posthooks'].append( (self.posthooklogger, (req,), {})) - resp = self.handle_request(req) + return self.handle_request(req)(env, start_response) else: # Lack of posthook support means that we have to log on the # start of the response, rather than after all the data has # been sent. This prevents logging client disconnects # differently than full transmissions. - resp = self.handle_request(req) + response = self.handle_request(req)(env, start_response) self.posthooklogger(env, req) - #if trans_id: - # resp.headers['x-trans-id'] = trans_id - return resp(env, start_response) + return response except Exception: print "EXCEPTION IN __call__: %s: %s" % \ (traceback.format_exc(), env) From 6b0e27adca53fff005beb802fece03356ce9f641 Mon Sep 17 00:00:00 2001 From: John Dickinson Date: Thu, 12 May 2011 11:13:08 -0500 Subject: [PATCH 8/8] added a CHANGELOG --- CHANGELOG | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000000..53fe7f6359 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,6 @@ +swift (x.x.x) + + * Transaction id for each request is returned in a response header + (X-Trans-ID). Setting the transaction id has moved from the proxy server to + the catch_errors middleware. Additionally, the internal header has changed + from X-CF-Trans-ID to X-Trans-ID.