1601
1605
self.assertEquals(res.status[:len(expected)], expected)
1602
1606
test_status_map((200, 200, 201, 201, -1), 201) # connect exc
1603
1607
# connect errors
1604
test_status_map((200, 200, 201, 201, Timeout()), 201)
1608
test_status_map((200, 200, Timeout(), 201, 201, ), 201)
1605
1609
test_status_map((200, 200, 201, 201, Exception()), 201)
1606
1610
# expect errors
1607
test_status_map((200, 200, 201, 201, (Timeout(), None)), 201)
1608
test_status_map((200, 200, 201, 201, (Exception(), None)), 201)
1611
test_status_map((200, 200, (Timeout(), None), 201, 201), 201)
1612
test_status_map((200, 200, (Exception(), None), 201, 201), 201)
1609
1613
# response errors
1610
test_status_map((200, 200, 201, 201, (100, Timeout())), 201)
1611
test_status_map((200, 200, 201, 201, (100, Exception())), 201)
1612
test_status_map((200, 200, 201, 201, 507), 201) # error limited
1613
test_status_map((200, 200, 201, -1, -1), 503)
1614
test_status_map((200, 200, 503, 503, -1), 503)
1614
test_status_map((200, 200, (100, Timeout()), 201, 201), 201)
1615
test_status_map((200, 200, (100, Exception()), 201, 201), 201)
1616
test_status_map((200, 200, 507, 201, 201), 201) # error limited
1617
test_status_map((200, 200, -1, 201, -1), 503)
1618
test_status_map((200, 200, 503, -1, 503), 503)
1616
1620
def test_PUT_send_exceptions(self):
1617
1621
with save_globals():
1728
1732
req_method, req_path, req_headers = req
1729
1733
self.assertEqual(method, req_method)
1730
1734
# caller can ignore leading path parts
1731
self.assertTrue(req_path.endswith(path))
1735
self.assertTrue(req_path.endswith(path),
1736
'expected path to end with %s, it was %s' % (
1732
1738
headers = headers or {}
1733
1739
# caller can ignore some headers
1734
1740
for k, v in headers.items():
1735
1741
self.assertEqual(req_headers[k], v)
1736
1742
account_request = backend_requests.pop(0)
1737
check_request(account_request, method='HEAD', path='/sda/1/a')
1743
check_request(account_request, method='HEAD', path='/sda/0/a')
1738
1744
container_request = backend_requests.pop(0)
1739
check_request(container_request, method='HEAD', path='/sda/1/a/c')
1740
for i, (device, request) in enumerate(zip(('sda', 'sdb', 'sdc'),
1745
check_request(container_request, method='HEAD', path='/sda/0/a/c')
1746
# make sure backend requests included expected container headers
1747
container_headers = {}
1748
for request in backend_requests:
1749
req_headers = request[2]
1750
device = req_headers['x-container-device']
1751
host = req_headers['x-container-host']
1752
container_headers[device] = host
1742
1753
expectations = {
1743
1754
'method': 'POST',
1744
'path': '/%s/1/a/c/o' % device,
1746
'X-Container-Host': '10.0.0.%d:100%d' % (i, i),
1747
'X-Container-Partition': '1',
1757
'X-Container-Partition': '0',
1748
1758
'Connection': 'close',
1749
1759
'User-Agent': 'proxy-server %s' % os.getpid(),
1750
1760
'Host': 'localhost:80',
1751
'X-Container-Device': device,
1752
1761
'Referer': 'POST http://localhost/v1/a/c/o',
1753
1762
'X-Object-Meta-Color': 'Blue',
1763
'X-Backend-Storage-Policy-Index': '1'
1757
1766
check_request(request, **expectations)
1769
for i, device in enumerate(['sda', 'sdb', 'sdc']):
1770
expected[device] = '10.0.0.%d:100%d' % (i, i)
1771
self.assertEqual(container_headers, expected)
1759
1773
# and again with policy override
1760
1774
self.app.memcache.store = {}
1761
1775
backend_requests = []
1762
1776
req = Request.blank('/v1/a/c/o', {}, method='POST',
1763
1777
headers={'X-Object-Meta-Color': 'Blue',
1778
'X-Backend-Storage-Policy-Index': 0})
1765
1779
with mocked_http_conn(
1766
1780
200, 200, 202, 202, 202,
1767
1781
headers=resp_headers, give_connect=capture_requests
1796
1810
self.assertRaises(StopIteration, fake_conn.code_iter.next)
1797
1811
self.assertEqual(resp.status_int, 202)
1798
1812
self.assertEqual(len(backend_requests), 8)
1799
policy0 = {POLICY_INDEX: '0'}
1800
policy1 = {POLICY_INDEX: '1'}
1813
policy0 = {'X-Backend-Storage-Policy-Index': '0'}
1814
policy1 = {'X-Backend-Storage-Policy-Index': '1'}
1803
{'method': 'HEAD', 'path': '/1/a'},
1817
{'method': 'HEAD', 'path': '/0/a'},
1804
1818
# container info
1805
{'method': 'HEAD', 'path': '/1/a/c'},
1819
{'method': 'HEAD', 'path': '/0/a/c'},
1807
{'method': 'GET', 'path': '/1/a/c/o', 'headers': policy1},
1808
{'method': 'GET', 'path': '/1/a/c/o', 'headers': policy1},
1809
{'method': 'GET', 'path': '/1/a/c/o', 'headers': policy1},
1821
{'method': 'GET', 'path': '/0/a/c/o', 'headers': policy1},
1822
{'method': 'GET', 'path': '/0/a/c/o', 'headers': policy1},
1823
{'method': 'GET', 'path': '/0/a/c/o', 'headers': policy1},
1811
{'method': 'PUT', 'path': '/1/a/c/o', 'headers': policy0},
1812
{'method': 'PUT', 'path': '/1/a/c/o', 'headers': policy0},
1813
{'method': 'PUT', 'path': '/1/a/c/o', 'headers': policy0},
1825
{'method': 'PUT', 'path': '/0/a/c/o', 'headers': policy0},
1826
{'method': 'PUT', 'path': '/0/a/c/o', 'headers': policy0},
1827
{'method': 'PUT', 'path': '/0/a/c/o', 'headers': policy0},
1815
1829
for request, expectations in zip(backend_requests, expected):
1816
1830
check_request(request, **expectations)
2369
2383
collected_nodes.append(node)
2370
2384
self.assertEquals(len(collected_nodes), 9)
2386
# zero error-limited primary nodes -> no handoff warnings
2372
2387
self.app.log_handoffs = True
2373
2388
self.app.logger = FakeLogger()
2374
object_ring.max_more_nodes = 2
2375
partition, nodes = object_ring.get_nodes('account',
2378
collected_nodes = []
2379
for node in self.app.iter_nodes(object_ring,
2381
collected_nodes.append(node)
2382
self.assertEquals(len(collected_nodes), 5)
2384
self.app.logger.log_dict['warning'],
2385
[(('Handoff requested (1)',), {}),
2386
(('Handoff requested (2)',), {})])
2388
self.app.log_handoffs = False
2389
self.app.logger = FakeLogger()
2390
object_ring.max_more_nodes = 2
2391
partition, nodes = object_ring.get_nodes('account',
2394
collected_nodes = []
2395
for node in self.app.iter_nodes(object_ring,
2397
collected_nodes.append(node)
2398
self.assertEquals(len(collected_nodes), 5)
2389
self.app.request_node_count = lambda r: 7
2390
object_ring.max_more_nodes = 20
2391
partition, nodes = object_ring.get_nodes('account',
2394
collected_nodes = []
2395
for node in self.app.iter_nodes(object_ring, partition):
2396
collected_nodes.append(node)
2397
self.assertEquals(len(collected_nodes), 7)
2399
2398
self.assertEquals(self.app.logger.log_dict['warning'], [])
2399
self.assertEquals(self.app.logger.get_increments(), [])
2401
# one error-limited primary node -> one handoff warning
2402
self.app.log_handoffs = True
2403
self.app.logger = FakeLogger()
2404
self.app.request_node_count = lambda r: 7
2405
object_ring.clear_errors()
2406
object_ring._devs[0]['errors'] = 999
2407
object_ring._devs[0]['last_error'] = 2 ** 63 - 1
2409
collected_nodes = []
2410
for node in self.app.iter_nodes(object_ring, partition):
2411
collected_nodes.append(node)
2412
self.assertEquals(len(collected_nodes), 7)
2413
self.assertEquals(self.app.logger.log_dict['warning'], [
2414
(('Handoff requested (5)',), {})])
2415
self.assertEquals(self.app.logger.get_increments(),
2418
# two error-limited primary nodes -> two handoff warnings
2419
self.app.log_handoffs = True
2420
self.app.logger = FakeLogger()
2421
self.app.request_node_count = lambda r: 7
2422
object_ring.clear_errors()
2424
object_ring._devs[i]['errors'] = 999
2425
object_ring._devs[i]['last_error'] = 2 ** 63 - 1
2427
collected_nodes = []
2428
for node in self.app.iter_nodes(object_ring, partition):
2429
collected_nodes.append(node)
2430
self.assertEquals(len(collected_nodes), 7)
2431
self.assertEquals(self.app.logger.log_dict['warning'], [
2432
(('Handoff requested (5)',), {}),
2433
(('Handoff requested (6)',), {})])
2434
self.assertEquals(self.app.logger.get_increments(),
2438
# all error-limited primary nodes -> four handoff warnings,
2439
# plus a handoff-all metric
2440
self.app.log_handoffs = True
2441
self.app.logger = FakeLogger()
2442
self.app.request_node_count = lambda r: 10
2443
object_ring.set_replicas(4) # otherwise we run out of handoffs
2444
object_ring.clear_errors()
2446
object_ring._devs[i]['errors'] = 999
2447
object_ring._devs[i]['last_error'] = 2 ** 63 - 1
2449
collected_nodes = []
2450
for node in self.app.iter_nodes(object_ring, partition):
2451
collected_nodes.append(node)
2452
self.assertEquals(len(collected_nodes), 10)
2453
self.assertEquals(self.app.logger.log_dict['warning'], [
2454
(('Handoff requested (7)',), {}),
2455
(('Handoff requested (8)',), {}),
2456
(('Handoff requested (9)',), {}),
2457
(('Handoff requested (10)',), {})])
2458
self.assertEquals(self.app.logger.get_increments(),
2463
'handoff_all_count'])
2401
2466
object_ring.max_more_nodes = 0
4058
4124
self.assertEquals(res.status, '202 Fake')
4059
4125
self.assertEquals(req.headers.get('x-delete-at'),
4060
4126
str(int(t + 60)))
4062
self.app.object_post_as_copy = False
4063
controller = proxy_server.ObjectController(self.app, 'account',
4066
set_http_connect(200, 200, 202, 202, 202)
4067
self.app.memcache.store = {}
4068
req = Request.blank('/v1/a/c/o', {},
4069
headers={'Content-Type': 'foo/bar',
4070
'X-Delete-After': '60'})
4071
self.app.update_request(req)
4072
res = controller.POST(req)
4073
self.assertEquals(res.status, '202 Fake')
4074
self.assertEquals(req.headers.get('x-delete-at'),
4077
4128
time.time = orig_time
4653
4707
self.assertEqual(
4654
4708
seen_headers, [
4655
4709
{'X-Container-Host': '10.0.0.0:1000',
4656
'X-Container-Partition': '1',
4710
'X-Container-Partition': '0',
4657
4711
'X-Container-Device': 'sda'},
4658
4712
{'X-Container-Host': '10.0.0.1:1001',
4659
'X-Container-Partition': '1',
4713
'X-Container-Partition': '0',
4660
4714
'X-Container-Device': 'sdb'},
4661
4715
{'X-Container-Host': '10.0.0.2:1002',
4662
'X-Container-Partition': '1',
4716
'X-Container-Partition': '0',
4663
4717
'X-Container-Device': 'sdc'}])
4665
4719
def test_PUT_x_container_headers_with_fewer_container_replicas(self):
4697
4751
self.assertEqual(
4698
4752
seen_headers, [
4699
4753
{'X-Container-Host': '10.0.0.0:1000,10.0.0.3:1003',
4700
'X-Container-Partition': '1',
4754
'X-Container-Partition': '0',
4701
4755
'X-Container-Device': 'sda,sdd'},
4702
4756
{'X-Container-Host': '10.0.0.1:1001',
4703
'X-Container-Partition': '1',
4757
'X-Container-Partition': '0',
4704
4758
'X-Container-Device': 'sdb'},
4705
4759
{'X-Container-Host': '10.0.0.2:1002',
4706
'X-Container-Partition': '1',
4760
'X-Container-Partition': '0',
4707
4761
'X-Container-Device': 'sdc'}])
4709
4763
def test_POST_x_container_headers_with_more_container_replicas(self):
4721
4775
self.assertEqual(
4722
4776
seen_headers, [
4723
4777
{'X-Container-Host': '10.0.0.0:1000,10.0.0.3:1003',
4724
'X-Container-Partition': '1',
4778
'X-Container-Partition': '0',
4725
4779
'X-Container-Device': 'sda,sdd'},
4726
4780
{'X-Container-Host': '10.0.0.1:1001',
4727
'X-Container-Partition': '1',
4781
'X-Container-Partition': '0',
4728
4782
'X-Container-Device': 'sdb'},
4729
4783
{'X-Container-Host': '10.0.0.2:1002',
4730
'X-Container-Partition': '1',
4784
'X-Container-Partition': '0',
4731
4785
'X-Container-Device': 'sdc'}])
4733
4787
def test_DELETE_x_container_headers_with_more_container_replicas(self):
4744
4798
self.assertEqual(seen_headers, [
4745
4799
{'X-Container-Host': '10.0.0.0:1000,10.0.0.3:1003',
4746
'X-Container-Partition': '1',
4800
'X-Container-Partition': '0',
4747
4801
'X-Container-Device': 'sda,sdd'},
4748
4802
{'X-Container-Host': '10.0.0.1:1001',
4749
'X-Container-Partition': '1',
4803
'X-Container-Partition': '0',
4750
4804
'X-Container-Device': 'sdb'},
4751
4805
{'X-Container-Host': '10.0.0.2:1002',
4752
'X-Container-Partition': '1',
4806
'X-Container-Partition': '0',
4753
4807
'X-Container-Device': 'sdc'}
4776
4829
self.assertEqual(seen_headers, [
4777
4830
{'X-Delete-At-Host': '10.0.0.0:1000',
4778
4831
'X-Delete-At-Container': delete_at_container,
4779
'X-Delete-At-Partition': '1',
4832
'X-Delete-At-Partition': '0',
4780
4833
'X-Delete-At-Device': 'sda'},
4781
4834
{'X-Delete-At-Host': '10.0.0.1:1001',
4782
4835
'X-Delete-At-Container': delete_at_container,
4783
'X-Delete-At-Partition': '1',
4836
'X-Delete-At-Partition': '0',
4784
4837
'X-Delete-At-Device': 'sdb'},
4785
4838
{'X-Delete-At-Host': None,
4786
4839
'X-Delete-At-Container': None,
4812
4864
self.assertEqual(seen_headers, [
4813
4865
{'X-Delete-At-Host': '10.0.0.0:1000,10.0.0.3:1003',
4814
4866
'X-Delete-At-Container': delete_at_container,
4815
'X-Delete-At-Partition': '1',
4867
'X-Delete-At-Partition': '0',
4816
4868
'X-Delete-At-Device': 'sda,sdd'},
4817
4869
{'X-Delete-At-Host': '10.0.0.1:1001',
4818
4870
'X-Delete-At-Container': delete_at_container,
4819
'X-Delete-At-Partition': '1',
4871
'X-Delete-At-Partition': '0',
4820
4872
'X-Delete-At-Device': 'sdb'},
4821
4873
{'X-Delete-At-Host': '10.0.0.2:1002',
4822
4874
'X-Delete-At-Container': delete_at_container,
4823
'X-Delete-At-Partition': '1',
4875
'X-Delete-At-Partition': '0',
4824
4876
'X-Delete-At-Device': 'sdc'}
4859
4911
self.assertEqual(controller._convert_policy_to_index(req), None)
4860
4912
# negative test
4861
4913
req = Request.blank('/a/c', headers={'Content-Length': '0',
4862
'Content-Type': 'text/plain', POLICY: 'nada'})
4914
'Content-Type': 'text/plain',
4915
'X-Storage-Policy': 'nada'})
4863
4916
self.assertRaises(HTTPException, controller._convert_policy_to_index,
4865
4918
# storage policy two is deprecated
4866
4919
req = Request.blank('/a/c', headers={'Content-Length': '0',
4867
4920
'Content-Type': 'text/plain',
4921
'X-Storage-Policy': 'two'})
4869
4922
self.assertRaises(HTTPException, controller._convert_policy_to_index,
4874
4927
req = Request.blank('/v1/a/c')
4875
4928
with mocked_http_conn(
4877
headers={POLICY_INDEX: int(policy)},
4930
headers={'X-Backend-Storage-Policy-Index': int(policy)},
4878
4931
) as fake_conn:
4879
4932
resp = req.get_response(self.app)
4880
4933
self.assertRaises(StopIteration, fake_conn.code_iter.next)
4881
4934
self.assertEqual(resp.status_int, 200)
4882
self.assertEqual(resp.headers[POLICY], policy.name)
4935
self.assertEqual(resp.headers['X-Storage-Policy'], policy.name)
4884
4937
def test_no_convert_index_to_name_when_container_not_found(self):
4885
4938
policy = random.choice(list(POLICIES))
4886
4939
req = Request.blank('/v1/a/c')
4887
4940
with mocked_http_conn(
4888
4941
200, 404, 404, 404,
4889
headers={POLICY_INDEX: int(policy)}) as fake_conn:
4942
headers={'X-Backend-Storage-Policy-Index':
4943
int(policy)}) as fake_conn:
4890
4944
resp = req.get_response(self.app)
4891
4945
self.assertRaises(StopIteration, fake_conn.code_iter.next)
4892
4946
self.assertEqual(resp.status_int, 404)
4893
self.assertEqual(resp.headers[POLICY], None)
4947
self.assertEqual(resp.headers['X-Storage-Policy'], None)
4895
4949
def test_error_convert_index_to_name(self):
4896
4950
req = Request.blank('/v1/a/c')
4897
4951
with mocked_http_conn(
4899
headers={POLICY_INDEX: '-1'}) as fake_conn:
4953
headers={'X-Backend-Storage-Policy-Index': '-1'}) as fake_conn:
4900
4954
resp = req.get_response(self.app)
4901
4955
self.assertRaises(StopIteration, fake_conn.code_iter.next)
4902
4956
self.assertEqual(resp.status_int, 200)
4903
self.assertEqual(resp.headers[POLICY], None)
4957
self.assertEqual(resp.headers['X-Storage-Policy'], None)
4904
4958
error_lines = self.app.logger.get_lines_for_level('error')
4905
4959
self.assertEqual(2, len(error_lines))
4906
4960
for msg in error_lines:
5162
5219
self.app.max_containers_per_account = 12345
5163
5220
controller = proxy_server.ContainerController(self.app, 'account',
5165
self.assert_status_map(controller.PUT, (201, 201, 201), 403,
5222
self.assert_status_map(controller.PUT,
5223
(200, 200, 201, 201, 201), 201,
5224
missing_container=True)
5226
controller = proxy_server.ContainerController(self.app, 'account',
5229
self.assert_status_map(controller.PUT, (200, 404, 404, 404), 403,
5166
5230
missing_container=True)
5168
5232
self.app.max_containers_per_account = 12345
5811
5875
200, 201, 201, 201) # HEAD PUT PUT PUT
5812
5876
self.assertEqual(seen_headers, [
5813
5877
{'X-Account-Host': '10.0.0.0:1000',
5814
'X-Account-Partition': '1',
5878
'X-Account-Partition': '0',
5815
5879
'X-Account-Device': 'sda'},
5816
5880
{'X-Account-Host': '10.0.0.1:1001',
5817
'X-Account-Partition': '1',
5881
'X-Account-Partition': '0',
5818
5882
'X-Account-Device': 'sdb'},
5819
5883
{'X-Account-Host': None,
5820
5884
'X-Account-Partition': None,
5831
5895
200, 201, 201, 201) # HEAD PUT PUT PUT
5832
5896
self.assertEqual(seen_headers, [
5833
5897
{'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
5834
'X-Account-Partition': '1',
5898
'X-Account-Partition': '0',
5835
5899
'X-Account-Device': 'sda,sdd'},
5836
5900
{'X-Account-Host': '10.0.0.1:1001',
5837
'X-Account-Partition': '1',
5901
'X-Account-Partition': '0',
5838
5902
'X-Account-Device': 'sdb'},
5839
5903
{'X-Account-Host': '10.0.0.2:1002',
5840
'X-Account-Partition': '1',
5904
'X-Account-Partition': '0',
5841
5905
'X-Account-Device': 'sdc'}
5851
5915
200, 204, 204, 204) # HEAD DELETE DELETE DELETE
5852
5916
self.assertEqual(seen_headers, [
5853
5917
{'X-Account-Host': '10.0.0.0:1000',
5854
'X-Account-Partition': '1',
5918
'X-Account-Partition': '0',
5855
5919
'X-Account-Device': 'sda'},
5856
5920
{'X-Account-Host': '10.0.0.1:1001',
5857
'X-Account-Partition': '1',
5921
'X-Account-Partition': '0',
5858
5922
'X-Account-Device': 'sdb'},
5859
5923
{'X-Account-Host': None,
5860
5924
'X-Account-Partition': None,
5871
5935
200, 204, 204, 204) # HEAD DELETE DELETE DELETE
5872
5936
self.assertEqual(seen_headers, [
5873
5937
{'X-Account-Host': '10.0.0.0:1000,10.0.0.3:1003',
5874
'X-Account-Partition': '1',
5938
'X-Account-Partition': '0',
5875
5939
'X-Account-Device': 'sda,sdd'},
5876
5940
{'X-Account-Host': '10.0.0.1:1001',
5877
'X-Account-Partition': '1',
5941
'X-Account-Partition': '0',
5878
5942
'X-Account-Device': 'sdb'},
5879
5943
{'X-Account-Host': '10.0.0.2:1002',
5880
'X-Account-Partition': '1',
5944
'X-Account-Partition': '0',
5881
5945
'X-Account-Device': 'sdc'}