From 457cea864c4b49bcf79327369c0bc5ebc24668e2 Mon Sep 17 00:00:00 2001 From: Samuel Merritt Date: Fri, 11 Mar 2016 16:15:17 -0800 Subject: [PATCH] Handle IPv6 addresses in swift-get-nodes. The curl commands needed a little tweaking. Change-Id: I6551d65241950c65e7160587cc414deb4a2122f5 Closes-Bug: 1555860 --- swift/cli/info.py | 44 ++++++++++++------ test/unit/cli/test_info.py | 92 +++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/swift/cli/info.py b/swift/cli/info.py index ba02cfd25a..23dbdea401 100644 --- a/swift/cli/info.py +++ b/swift/cli/info.py @@ -20,7 +20,7 @@ from six.moves import urllib from swift.common.utils import hash_path, storage_directory, \ Timestamp -from swift.common.ring import Ring +from swift.common.ring import Ring, utils as ring_utils from swift.common.request_helpers import is_sys_meta, is_user_meta, \ strip_sys_meta_prefix, strip_user_meta_prefix from swift.account.backend import AccountBroker, DATADIR as ABDATADIR @@ -37,6 +37,32 @@ class InfoSystemExit(Exception): pass +def curl_head_command(ip, port, device, part, target, policy_index): + """ + Provide a string that is a well formatted curl command to HEAD an object + on a storage node. + + :param ip: the ip of the node + :param port: the port of the node + :param device: the device of the node + :param target: the path of the target resource + :param policy_index: the policy_index of the target resource (can be None) + + :returns: a string, a well formatted curl command + """ + if ring_utils.is_valid_ipv6(ip): + formatted_ip = '[%s]' % ip + else: + formatted_ip = ip + + cmd = 'curl -g -I -XHEAD "http://%s:%s/%s/%s/%s"' % ( + formatted_ip, port, device, part, urllib.parse.quote(target)) + if policy_index is not None: + cmd += ' -H "%s: %s"' % ('X-Backend-Storage-Policy-Index', + policy_index) + return cmd + + def print_ring_locations(ring, datadir, account, container=None, obj=None, tpart=None, all_nodes=False, policy_index=None): """ @@ -99,20 +125,12 @@ def print_ring_locations(ring, datadir, account, container=None, obj=None, print("\n") for node in primary_nodes: - cmd = 'curl -I -XHEAD "http://%s:%s/%s/%s/%s"' \ - % (node['ip'], node['port'], node['device'], part, - urllib.parse.quote(target)) - if policy_index is not None: - cmd += ' -H "%s: %s"' % ('X-Backend-Storage-Policy-Index', - policy_index) + cmd = curl_head_command(node['ip'], node['port'], node['device'], + part, target, policy_index) print(cmd) for node in handoff_nodes: - cmd = 'curl -I -XHEAD "http://%s:%s/%s/%s/%s"' \ - % (node['ip'], node['port'], node['device'], part, - urllib.parse.quote(target)) - if policy_index is not None: - cmd += ' -H "%s: %s"' % ('X-Backend-Storage-Policy-Index', - policy_index) + cmd = curl_head_command(node['ip'], node['port'], node['device'], + part, target, policy_index) cmd += ' # [Handoff]' print(cmd) diff --git a/test/unit/cli/test_info.py b/test/unit/cli/test_info.py index a97362dcbc..ee5922d3d0 100644 --- a/test/unit/cli/test_info.py +++ b/test/unit/cli/test_info.py @@ -34,7 +34,8 @@ from swift.obj.diskfile import write_metadata @patch_policies([StoragePolicy(0, 'zero', True), StoragePolicy(1, 'one', False), - StoragePolicy(2, 'two', False)]) + StoragePolicy(2, 'two', False), + StoragePolicy(3, 'three', False)]) class TestCliInfoBase(unittest.TestCase): def setUp(self): self.orig_hp = utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX @@ -72,6 +73,13 @@ class TestCliInfoBase(unittest.TestCase): # ... and another for policy 2 self.two_ring_path = os.path.join(self.testdir, 'object-2.ring.gz') write_fake_ring(self.two_ring_path, *object_devs) + # ... and one for policy 3 with some v6 IPs in it + object_devs_ipv6 = [ + {'ip': 'feed:face::dead:beef', 'port': 42}, + {'ip': 'deca:fc0f:feeb:ad11::1', 'port': 43} + ] + self.three_ring_path = os.path.join(self.testdir, 'object-3.ring.gz') + write_fake_ring(self.three_ring_path, *object_devs_ipv6) def tearDown(self): utils.HASH_PATH_PREFIX, utils.HASH_PATH_SUFFIX = self.orig_hp @@ -569,6 +577,88 @@ class TestPrintObjFullMeta(TestCliInfoBase): os.chdir(cwd) self.assertTrue('X-Backend-Storage-Policy-Index: 1' in out.getvalue()) + def test_print_obj_curl_command_ipv4(self): + # Note: policy 2 has IPv4 addresses in its ring + datafile2 = os.path.join( + self.testdir, + 'sda', 'objects-2', '1', 'ea8', + 'db4449e025aca992307c7c804a67eea8', '1402017884.18202.data') + utils.mkdirs(os.path.dirname(datafile2)) + with open(datafile2, 'wb') as fp: + md = {'name': '/AUTH_admin/c/obj', + 'Content-Type': 'application/octet-stream', + 'ETag': 'd41d8cd98f00b204e9800998ecf8427e', + 'Content-Length': 0} + write_metadata(fp, md) + + object_ring = ring.Ring(self.testdir, ring_name='object-2') + part, nodes = object_ring.get_nodes('AUTH_admin', 'c', 'obj') + node = nodes[0] + + out = StringIO() + hash_dir = os.path.dirname(datafile2) + file_name = os.path.basename(datafile2) + + # Change working directory to object hash dir + cwd = os.getcwd() + try: + os.chdir(hash_dir) + with mock.patch('sys.stdout', out): + print_obj(file_name, swift_dir=self.testdir) + finally: + os.chdir(cwd) + + exp_curl = ( + 'curl -g -I -XHEAD ' + '"http://{host}:{port}/{device}/{part}/AUTH_admin/c/obj" ' + '-H "X-Backend-Storage-Policy-Index: 2"').format( + host=node['ip'], + port=node['port'], + device=node['device'], + part=part) + self.assertIn(exp_curl, out.getvalue()) + + def test_print_obj_curl_command_ipv6(self): + # Note: policy 3 has IPv6 addresses in its ring + datafile3 = os.path.join( + self.testdir, + 'sda', 'objects-3', '1', 'ea8', + 'db4449e025aca992307c7c804a67eea8', '1402017884.18202.data') + utils.mkdirs(os.path.dirname(datafile3)) + with open(datafile3, 'wb') as fp: + md = {'name': '/AUTH_admin/c/obj', + 'Content-Type': 'application/octet-stream', + 'ETag': 'd41d8cd98f00b204e9800998ecf8427e', + 'Content-Length': 0} + write_metadata(fp, md) + + object_ring = ring.Ring(self.testdir, ring_name='object-3') + part, nodes = object_ring.get_nodes('AUTH_admin', 'c', 'obj') + node = nodes[0] + + out = StringIO() + hash_dir = os.path.dirname(datafile3) + file_name = os.path.basename(datafile3) + + # Change working directory to object hash dir + cwd = os.getcwd() + try: + os.chdir(hash_dir) + with mock.patch('sys.stdout', out): + print_obj(file_name, swift_dir=self.testdir) + finally: + os.chdir(cwd) + + exp_curl = ( + 'curl -g -I -XHEAD ' + '"http://[{host}]:{port}' + '/{device}/{part}/AUTH_admin/c/obj" ').format( + host=node['ip'], + port=node['port'], + device=node['device'], + part=part) + self.assertIn(exp_curl, out.getvalue()) + def test_print_obj_meta_and_ts_files(self): # verify that print_obj will also read from meta and ts files base = os.path.splitext(self.datafile)[0]