diff --git a/swift/proxy/controllers/obj.py b/swift/proxy/controllers/obj.py index 5b4c6c1f4a..33f78364bb 100644 --- a/swift/proxy/controllers/obj.py +++ b/swift/proxy/controllers/obj.py @@ -448,14 +448,15 @@ class BaseObjectController(Controller): headers = [self.generate_request_headers(req, additional=req.headers) for _junk in range(n_outgoing)] - def set_container_update(index, container): + def set_container_update(index, container_node): + ip, port = get_ip_port(container_node, headers[index]) headers[index]['X-Container-Partition'] = container_partition headers[index]['X-Container-Host'] = csv_append( headers[index].get('X-Container-Host'), - '%(ip)s:%(port)s' % container) + '%(ip)s:%(port)s' % {'ip': ip, 'port': port}) headers[index]['X-Container-Device'] = csv_append( headers[index].get('X-Container-Device'), - container['device']) + container_node['device']) if container_path: headers[index]['X-Backend-Quoted-Container-Path'] = quote( container_path) @@ -468,11 +469,12 @@ class BaseObjectController(Controller): # will eat the update and move it as a misplaced object. def set_delete_at_headers(index, delete_at_node): + ip, port = get_ip_port(delete_at_node, headers[index]) headers[index]['X-Delete-At-Container'] = delete_at_container headers[index]['X-Delete-At-Partition'] = delete_at_partition headers[index]['X-Delete-At-Host'] = csv_append( headers[index].get('X-Delete-At-Host'), - '%(ip)s:%(port)s' % delete_at_node) + '%(ip)s:%(port)s' % {'ip': ip, 'port': port}) headers[index]['X-Delete-At-Device'] = csv_append( headers[index].get('X-Delete-At-Device'), delete_at_node['device']) diff --git a/test/unit/proxy/controllers/test_obj.py b/test/unit/proxy/controllers/test_obj.py index ddb144474b..0a8c7fc888 100644 --- a/test/unit/proxy/controllers/test_obj.py +++ b/test/unit/proxy/controllers/test_obj.py @@ -434,6 +434,67 @@ class CommonObjectControllerMixin(BaseObjectControllerMixin): resp = req.get_response(self.app) self.assertEqual(resp.status_int, 204) + def test_object_DELETE_backend_update_container_ip_default(self): + self.policy.object_ring = FakeRing(separate_replication=True) + self.app.container_ring = FakeRing(separate_replication=True) + # sanity, devs have different ip & replication_ip + for ring in (self.policy.object_ring, self.app.container_ring): + for dev in ring.devs: + self.assertNotEqual(dev['ip'], dev['replication_ip']) + req = swift.common.swob.Request.blank('/v1/a/c/o', method='DELETE') + codes = [204] * self.replicas() + with mocked_http_conn(*codes) as log: + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 204) + + # sanity, object hosts use node ip/port + object_hosts = {'%(ip)s:%(port)s' % req for req in log.requests} + object_ring = self.app.get_object_ring(int(self.policy)) + part, object_nodes = object_ring.get_nodes('a', 'c', 'o') + expected_object_hosts = {'%(ip)s:%(port)s' % n for n in object_nodes} + self.assertEqual(object_hosts, expected_object_hosts) + + # container hosts use node ip/port + container_hosts = {req['headers']['x-container-host'] + for req in log.requests} + part, container_nodes = self.app.container_ring.get_nodes('a', 'c') + expected_container_hosts = {'%(ip)s:%(port)s' % n + for n in container_nodes} + self.assertEqual(container_hosts, expected_container_hosts) + + def test_repl_object_DELETE_backend_update_container_repl_ip(self): + self.policy.object_ring = FakeRing(separate_replication=True) + self.app.container_ring = FakeRing(separate_replication=True) + # sanity, devs have different ip & replication_ip + for ring in (self.policy.object_ring, self.app.container_ring): + for dev in ring.devs: + self.assertNotEqual(dev['ip'], dev['replication_ip']) + req = swift.common.swob.Request.blank( + '/v1/a/c/o', method='DELETE', headers={ + 'x-backend-use-replication-network': 'true'}) + codes = [204] * self.replicas() + with mocked_http_conn(*codes) as log: + resp = req.get_response(self.app) + self.assertEqual(resp.status_int, 204) + + # sanity, object hosts use node replication ip/port + object_hosts = {'%(ip)s:%(port)s' % req for req in log.requests} + object_ring = self.app.get_object_ring(int(self.policy)) + part, object_nodes = object_ring.get_nodes('a', 'c', 'o') + expected_object_hosts = { + '%(replication_ip)s:%(replication_port)s' % n + for n in object_nodes} + self.assertEqual(object_hosts, expected_object_hosts) + + # container hosts use node replication ip/port + container_hosts = {req['headers']['x-container-host'] + for req in log.requests} + part, container_nodes = self.app.container_ring.get_nodes('a', 'c') + expected_container_hosts = { + '%(replication_ip)s:%(replication_port)s' % n + for n in container_nodes} + self.assertEqual(container_hosts, expected_container_hosts) + def test_DELETE_missing_one(self): # Obviously this test doesn't work if we're testing 1 replica. # In that case, we don't have any failovers to check. diff --git a/test/unit/proxy/test_server.py b/test/unit/proxy/test_server.py index abe5072ee8..24f67185ae 100644 --- a/test/unit/proxy/test_server.py +++ b/test/unit/proxy/test_server.py @@ -4176,20 +4176,32 @@ class TestReplicatedObjectController( StoragePolicy(1, 'one', object_ring=FakeRing()), ]) def test_POST_backend_headers(self): + self.app = proxy_server.Application( + {}, + account_ring=FakeRing(separate_replication=True), + container_ring=FakeRing(separate_replication=True)) + + part = self.app.container_ring.get_part('a', 'c') + nodes = self.app.container_ring.get_part_nodes(part) + self.assertNotEqual(nodes[0]['ip'], nodes[0]['replication_ip']) + # reset the router post patch_policies self.app.obj_controller_router = proxy_server.ObjectControllerRouter() self.app.sort_nodes = lambda nodes, *args, **kwargs: nodes - def do_test(resp_headers): + def do_test(resp_headers, use_replication=False): backend_requests = [] def capture_requests(ip, port, method, path, headers, *args, **kwargs): backend_requests.append((method, path, headers)) + replication_aware = 'true' if use_replication else 'false' req = Request.blank('/v1/a/c/o', {}, method='POST', headers={'X-Object-Meta-Color': 'Blue', - 'Content-Type': 'text/plain'}) + 'Content-Type': 'text/plain', + 'x-backend-use-replication-network': + replication_aware}) # we want the container_info response to says a policy index of 1 with mocked_http_conn( @@ -4244,7 +4256,10 @@ class TestReplicatedObjectController( expected = {} for i, device in enumerate(['sda', 'sdb', 'sdc']): - expected[device] = '10.0.0.%d:100%d' % (i, i) + if use_replication: + expected[device] = '10.0.1.%d:110%d' % (i, i) + else: + expected[device] = '10.0.0.%d:100%d' % (i, i) self.assertEqual(container_headers, expected) # and again with policy override @@ -4277,6 +4292,7 @@ class TestReplicatedObjectController( do_test(resp_headers) resp_headers['X-Backend-Sharding-State'] = 'unsharded' do_test(resp_headers) + do_test(resp_headers, use_replication=True) def _check_request(self, req, method, path, headers=None, params=None): self.assertEqual(method, req['method'])