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
This commit is contained in:
Alistair Coles 2016-04-06 11:48:48 +01:00
parent 950b601a9c
commit 5d56f40f04
2 changed files with 45 additions and 5 deletions

View File

@ -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())

View File

@ -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')