From 5d56f40f04fa9211a2e392d667fe395f38d2cca5 Mon Sep 17 00:00:00 2001 From: Alistair Coles Date: Wed, 6 Apr 2016 11:48:48 +0100 Subject: [PATCH] Make DirectClientException report correct ip and port When direct_client.direct_get_suffix_hashes raises a DirectClientException the exception message and variables should report the replication_ip and replication_port, as opposed to the ip and port values reported for all other case when the exception is raised. Add option to override ip and port reported in DirectClientException. Also adds unit tests to verify both cases. Related-Bug: 1566395 Change-Id: If3d952847c7199f4e9f6164858085367266386d2 --- swift/common/direct_client.py | 14 +++++++--- test/unit/common/test_direct_client.py | 36 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/swift/common/direct_client.py b/swift/common/direct_client.py index a507901edc..94be486d74 100644 --- a/swift/common/direct_client.py +++ b/swift/common/direct_client.py @@ -39,13 +39,16 @@ from swift.common.utils import quote class DirectClientException(ClientException): - def __init__(self, stype, method, node, part, path, resp): + def __init__(self, stype, method, node, part, path, resp, host=None): + # host can be used to override the node ip and port reported in + # the exception + host = host if host is not None else node full_path = quote('/%s/%s%s' % (node['device'], part, path)) msg = '%s server %s:%s direct %s %r gave status %s' % ( - stype, node['ip'], node['port'], method, full_path, resp.status) + stype, host['ip'], host['port'], method, full_path, resp.status) headers = HeaderKeyDict(resp.getheaders()) super(DirectClientException, self).__init__( - msg, http_host=node['ip'], http_port=node['port'], + msg, http_host=host['ip'], http_port=host['port'], http_device=node['device'], http_status=resp.status, http_reason=resp.reason, http_headers=headers) @@ -489,7 +492,10 @@ def direct_get_suffix_hashes(node, part, suffixes, conn_timeout=5, resp = conn.getresponse() if not is_success(resp.status): raise DirectClientException('Object', 'REPLICATE', - node, part, path, resp) + node, part, path, resp, + host={'ip': node['replication_ip'], + 'port': node['replication_port']} + ) return pickle.loads(resp.read()) diff --git a/test/unit/common/test_direct_client.py b/test/unit/common/test_direct_client.py index 2bcc94c13d..503a941186 100644 --- a/test/unit/common/test_direct_client.py +++ b/test/unit/common/test_direct_client.py @@ -26,9 +26,10 @@ import six from six.moves import urllib from swift.common import direct_client +from swift.common.direct_client import DirectClientException from swift.common.exceptions import ClientException from swift.common.header_key_dict import HeaderKeyDict -from swift.common.utils import Timestamp +from swift.common.utils import Timestamp, quote from swift.common.swob import RESPONSE_REASONS from swift.common.storage_policy import POLICIES from six.moves.http_client import HTTPException @@ -631,6 +632,28 @@ class TestDirectClient(unittest.TestCase): self.assertEqual(conn.port, '7000') self.assertEqual(data, resp) + def _test_direct_get_suffix_hashes_fail(self, status_code): + with mocked_http_conn(status_code): + with self.assertRaises(DirectClientException) as cm: + direct_client.direct_get_suffix_hashes( + self.node, self.part, ['a83', 'b52']) + self.assertIn('REPLICATE', cm.exception.message) + self.assertIn(quote('/%s/%s/a83-b52' + % (self.node['device'], self.part)), + cm.exception.message) + self.assertIn(self.node['replication_ip'], cm.exception.message) + self.assertIn(self.node['replication_port'], cm.exception.message) + self.assertEqual(self.node['replication_ip'], cm.exception.http_host) + self.assertEqual(self.node['replication_port'], cm.exception.http_port) + self.assertEqual(self.node['device'], cm.exception.http_device) + self.assertEqual(status_code, cm.exception.http_status) + + def test_direct_get_suffix_hashes_503(self): + self._test_direct_get_suffix_hashes_fail(503) + + def test_direct_get_suffix_hashes_507(self): + self._test_direct_get_suffix_hashes_fail(507) + def test_direct_put_object_with_content_length(self): contents = six.StringIO('123456') @@ -720,6 +743,17 @@ class TestDirectClient(unittest.TestCase): retries=2, error_log=logger.error) self.assertEqual('DELETE', conn.method) self.assertEqual(err_ctx.exception.http_status, 500) + self.assertIn('DELETE', err_ctx.exception.message) + self.assertIn(quote('/%s/%s/%s/%s/%s' + % (self.node['device'], self.part, self.account, + self.container, self.obj)), + err_ctx.exception.message) + self.assertIn(self.node['ip'], err_ctx.exception.message) + self.assertIn(self.node['port'], err_ctx.exception.message) + self.assertEqual(self.node['ip'], err_ctx.exception.http_host) + self.assertEqual(self.node['port'], err_ctx.exception.http_port) + self.assertEqual(self.node['device'], err_ctx.exception.http_device) + self.assertEqual(500, err_ctx.exception.http_status) self.assertEqual([mock.call(1), mock.call(2)], mock_sleep.call_args_list) error_lines = logger.get_lines_for_level('error')

AltStyle によって変換されたページ (->オリジナル) /