14
14
# See the License for the specific language governing permissions and
15
15
# limitations under the License.
20
from signal import SIGTERM
21
from subprocess import Popen
22
from time import sleep
17
from os import listdir
18
from os.path import join as path_join
19
from unittest import main, TestCase
23
20
from uuid import uuid4
22
from eventlet import GreenPool, Timeout
23
from sqlite3 import connect
27
24
from swiftclient import client
28
26
from swift.common import direct_client
29
27
from swift.common.utils import hash_path, readconf
31
from test.probe.common import get_to_final_state, kill_pids, reset_environment
34
class TestContainerFailures(unittest.TestCase):
28
from test.probe.common import get_to_final_state, kill_nonprimary_server, \
29
kill_server, kill_servers, reset_environment, start_server
32
def get_db_file_path(obj_dir):
33
files = sorted(listdir(obj_dir), reverse=True)
34
for filename in files:
35
if filename.endswith('db'):
36
return path_join(obj_dir, filename)
39
class TestContainerFailures(TestCase):
37
self.pids, self.port2server, self.account_ring, self.container_ring, \
38
self.object_ring, self.url, self.token, self.account = \
42
(self.pids, self.port2server, self.account_ring, self.container_ring,
43
self.object_ring, self.url, self.token,
44
self.account) = reset_environment()
41
46
def tearDown(self):
44
def test_first_node_fail(self):
45
container = 'container-%s' % uuid4()
46
client.put_container(self.url, self.token, container)
47
self.assert_(container in [c['name'] for c in
48
client.get_account(self.url, self.token)[1]])
51
client.put_object(self.url, self.token, container, object1, 'test')
52
self.assert_(container in [c['name'] for c in
53
client.get_account(self.url, self.token)[1]])
54
self.assert_(object1 in [o['name'] for o in
55
client.get_container(self.url, self.token, container)[1]])
57
cpart, cnodes = self.container_ring.get_nodes(self.account, container)
58
kill(self.pids[self.port2server[cnodes[0]['port']]], SIGTERM)
60
client.delete_object(self.url, self.token, container, object1)
61
self.assert_(container in [c['name'] for c in
62
client.get_account(self.url, self.token)[1]])
63
self.assert_(object1 not in [o['name'] for o in
64
client.get_container(self.url, self.token, container)[1]])
66
self.pids[self.port2server[cnodes[0]['port']]] = \
67
Popen(['swift-container-server',
68
'/etc/swift/container-server/%d.conf' %
69
((cnodes[0]['port'] - 6001) / 10)]).pid
71
self.assert_(container in [c['name'] for c in
72
client.get_account(self.url, self.token)[1]])
73
# This okay because the first node hasn't got the update that the
74
# object was deleted yet.
75
self.assert_(object1 in [o['name'] for o in
76
direct_client.direct_get_container(cnodes[0], cpart,
77
self.account, container)[1]])
79
# Unfortunately, the following might pass or fail, depending on the
80
# position of the account server associated with the first container
81
# server we had killed. If the associated happens to be the first
82
# account server, this'll pass, otherwise the first account server will
83
# serve the listing and not have the container.
84
# self.assert_(container in [c['name'] for c in
85
# client.get_account(self.url, self.token)[1]])
88
# This will work because at least one (in this case, just one) account
89
# server has to indicate the container exists for the put to continue.
90
client.put_object(self.url, self.token, container, object2, 'test')
91
# First node still doesn't know object1 was deleted yet; this is okay.
92
self.assert_(object1 in [o['name'] for o in
93
direct_client.direct_get_container(cnodes[0], cpart,
94
self.account, container)[1]])
95
# And, of course, our new object2 exists.
96
self.assert_(object2 in [o['name'] for o in
97
client.get_container(self.url, self.token, container)[1]])
100
# Our container delete never "finalized" because we started using it
101
# before the delete settled.
102
self.assert_(container in [c['name'] for c in
103
client.get_account(self.url, self.token)[1]])
104
# And, so our object2 should still exist and object1's delete should
106
self.assert_(object1 not in [o['name'] for o in
107
client.get_container(self.url, self.token, container)[1]])
108
self.assert_(object2 in [o['name'] for o in
109
client.get_container(self.url, self.token, container)[1]])
111
def test_second_node_fail(self):
112
container = 'container-%s' % uuid4()
113
client.put_container(self.url, self.token, container)
114
self.assert_(container in [c['name'] for c in
115
client.get_account(self.url, self.token)[1]])
118
client.put_object(self.url, self.token, container, object1, 'test')
119
self.assert_(container in [c['name'] for c in
120
client.get_account(self.url, self.token)[1]])
121
self.assert_(object1 in [o['name'] for o in
122
client.get_container(self.url, self.token, container)[1]])
124
cpart, cnodes = self.container_ring.get_nodes(self.account, container)
125
kill(self.pids[self.port2server[cnodes[1]['port']]], SIGTERM)
127
client.delete_object(self.url, self.token, container, object1)
128
self.assert_(container in [c['name'] for c in
129
client.get_account(self.url, self.token)[1]])
130
self.assert_(object1 not in [o['name'] for o in
131
client.get_container(self.url, self.token, container)[1]])
133
self.pids[self.port2server[cnodes[1]['port']]] = \
134
Popen(['swift-container-server',
135
'/etc/swift/container-server/%d.conf' %
136
((cnodes[1]['port'] - 6001) / 10)]).pid
138
self.assert_(container in [c['name'] for c in
139
client.get_account(self.url, self.token)[1]])
140
self.assert_(object1 not in [o['name'] for o in
141
client.get_container(self.url, self.token, container)[1]])
143
# Unfortunately, the following might pass or fail, depending on the
144
# position of the account server associated with the first container
145
# server we had killed. If the associated happens to be the first
146
# account server, this'll pass, otherwise the first account server will
147
# serve the listing and not have the container.
148
# self.assert_(container in [c['name'] for c in
149
# client.get_account(self.url, self.token)[1]])
152
# This will work because at least one (in this case, just one) account
153
# server has to indicate the container exists for the put to continue.
154
client.put_object(self.url, self.token, container, object2, 'test')
155
self.assert_(object1 not in [o['name'] for o in
156
direct_client.direct_get_container(cnodes[0], cpart,
157
self.account, container)[1]])
158
# And, of course, our new object2 exists.
159
self.assert_(object2 in [o['name'] for o in
160
client.get_container(self.url, self.token, container)[1]])
163
# Our container delete never "finalized" because we started using it
164
# before the delete settled.
165
self.assert_(container in [c['name'] for c in
166
client.get_account(self.url, self.token)[1]])
167
# And, so our object2 should still exist and object1's delete should
169
self.assert_(object1 not in [o['name'] for o in
170
client.get_container(self.url, self.token, container)[1]])
171
self.assert_(object2 in [o['name'] for o in
172
client.get_container(self.url, self.token, container)[1]])
174
def test_first_two_nodes_fail(self):
175
container = 'container-%s' % uuid4()
176
client.put_container(self.url, self.token, container)
177
self.assert_(container in [c['name'] for c in
178
client.get_account(self.url, self.token)[1]])
181
client.put_object(self.url, self.token, container, object1, 'test')
182
self.assert_(container in [c['name'] for c in
183
client.get_account(self.url, self.token)[1]])
184
self.assert_(object1 in [o['name'] for o in
185
client.get_container(self.url, self.token, container)[1]])
187
cpart, cnodes = self.container_ring.get_nodes(self.account, container)
189
kill(self.pids[self.port2server[cnodes[x]['port']]], SIGTERM)
191
client.delete_object(self.url, self.token, container, object1)
192
self.assert_(container in [c['name'] for c in
193
client.get_account(self.url, self.token)[1]])
194
self.assert_(object1 not in [o['name'] for o in
195
client.get_container(self.url, self.token, container)[1]])
198
self.pids[self.port2server[cnodes[x]['port']]] = \
199
Popen(['swift-container-server',
200
'/etc/swift/container-server/%d.conf' %
201
((cnodes[x]['port'] - 6001) / 10)]).pid
203
self.assert_(container in [c['name'] for c in
204
client.get_account(self.url, self.token)[1]])
205
# This okay because the first node hasn't got the update that the
206
# object was deleted yet.
207
self.assert_(object1 in [o['name'] for o in
208
direct_client.direct_get_container(cnodes[0], cpart,
209
self.account, container)[1]])
211
# This fails because all three nodes have to indicate deletion before
212
# we tell the user it worked. Since the first node 409s (it hasn't got
213
# the update that the object was deleted yet), the whole must 503
214
# (until every is synced up, then the delete would work).
217
client.delete_container(self.url, self.token, container)
218
except client.ClientException, err:
221
self.assert_(exc.http_status, 503)
222
# Unfortunately, the following might pass or fail, depending on the
223
# position of the account server associated with the first container
224
# server we had killed. If the associated happens to be the first
225
# account server, this'll pass, otherwise the first account server will
226
# serve the listing and not have the container.
227
# self.assert_(container in [c['name'] for c in
228
# client.get_account(self.url, self.token)[1]])
231
# This will work because at least one (in this case, just one) account
232
# server has to indicate the container exists for the put to continue.
233
client.put_object(self.url, self.token, container, object2, 'test')
234
# First node still doesn't know object1 was deleted yet; this is okay.
235
self.assert_(object1 in [o['name'] for o in
236
direct_client.direct_get_container(cnodes[0], cpart,
237
self.account, container)[1]])
238
# And, of course, our new object2 exists.
239
self.assert_(object2 in [o['name'] for o in
240
client.get_container(self.url, self.token, container)[1]])
243
# Our container delete never "finalized" because we started using it
244
# before the delete settled.
245
self.assert_(container in [c['name'] for c in
246
client.get_account(self.url, self.token)[1]])
247
# And, so our object2 should still exist and object1's delete should
249
self.assert_(object1 not in [o['name'] for o in
250
client.get_container(self.url, self.token, container)[1]])
251
self.assert_(object2 in [o['name'] for o in
252
client.get_container(self.url, self.token, container)[1]])
254
def test_last_two_nodes_fail(self):
255
container = 'container-%s' % uuid4()
256
client.put_container(self.url, self.token, container)
257
self.assert_(container in [c['name'] for c in
258
client.get_account(self.url, self.token)[1]])
261
client.put_object(self.url, self.token, container, object1, 'test')
262
self.assert_(container in [c['name'] for c in
263
client.get_account(self.url, self.token)[1]])
264
self.assert_(object1 in [o['name'] for o in
265
client.get_container(self.url, self.token, container)[1]])
267
cpart, cnodes = self.container_ring.get_nodes(self.account, container)
269
kill(self.pids[self.port2server[cnodes[x]['port']]], SIGTERM)
271
client.delete_object(self.url, self.token, container, object1)
272
self.assert_(container in [c['name'] for c in
273
client.get_account(self.url, self.token)[1]])
274
self.assert_(object1 not in [o['name'] for o in
275
client.get_container(self.url, self.token, container)[1]])
278
self.pids[self.port2server[cnodes[x]['port']]] = \
279
Popen(['swift-container-server',
280
'/etc/swift/container-server/%d.conf' %
281
((cnodes[x]['port'] - 6001) / 10)]).pid
283
self.assert_(container in [c['name'] for c in
284
client.get_account(self.url, self.token)[1]])
285
self.assert_(object1 not in [o['name'] for o in
286
direct_client.direct_get_container(cnodes[0], cpart,
287
self.account, container)[1]])
289
# This fails because all three nodes have to indicate deletion before
290
# we tell the user it worked. Since the first node 409s (it hasn't got
291
# the update that the object was deleted yet), the whole must 503
292
# (until every is synced up, then the delete would work).
295
client.delete_container(self.url, self.token, container)
296
except client.ClientException, err:
299
self.assert_(exc.http_status, 503)
300
# Unfortunately, the following might pass or fail, depending on the
301
# position of the account server associated with the first container
302
# server we had killed. If the associated happens to be the first
303
# account server, this'll pass, otherwise the first account server will
304
# serve the listing and not have the container.
305
# self.assert_(container in [c['name'] for c in
306
# client.get_account(self.url, self.token)[1]])
309
# This will work because at least one (in this case, just one) account
310
# server has to indicate the container exists for the put to continue.
311
client.put_object(self.url, self.token, container, object2, 'test')
312
self.assert_(object1 not in [o['name'] for o in
313
direct_client.direct_get_container(cnodes[0], cpart,
314
self.account, container)[1]])
315
# And, of course, our new object2 exists.
316
self.assert_(object2 in [o['name'] for o in
317
client.get_container(self.url, self.token, container)[1]])
320
# Our container delete never "finalized" because we started using it
321
# before the delete settled.
322
self.assert_(container in [c['name'] for c in
323
client.get_account(self.url, self.token)[1]])
324
# And, so our object2 should still exist and object1's delete should
326
self.assert_(object1 not in [o['name'] for o in
327
client.get_container(self.url, self.token, container)[1]])
328
self.assert_(object2 in [o['name'] for o in
329
client.get_container(self.url, self.token, container)[1]])
331
def _get_db_file_path(self, obj_dir):
332
files = sorted(os.listdir(obj_dir), reverse=True)
334
if file.endswith('db'):
335
return os.path.join(obj_dir, file)
47
kill_servers(self.port2server, self.pids)
49
def test_one_node_fails(self):
51
# Kill container1 servers excepting two of the primaries
53
# Restart other container1 primary server
54
# Create container1/object1 (allowed because at least server thinks the
56
# Get to a final state
57
# Assert all container1 servers indicate container1 is alive and
59
# Assert account level also indicates container1 is alive and
61
container1 = 'container-%s' % uuid4()
62
cpart, cnodes = self.container_ring.get_nodes(self.account, container1)
63
client.put_container(self.url, self.token, container1)
64
kill_nonprimary_server(cnodes, self.port2server, self.pids)
65
kill_server(cnodes[0]['port'], self.port2server, self.pids)
66
client.delete_container(self.url, self.token, container1)
67
start_server(cnodes[0]['port'], self.port2server, self.pids)
68
client.put_object(self.url, self.token, container1, 'object1', '123')
72
[o['name'] for o in direct_client.direct_get_container(
73
cnode, cpart, self.account, container1)[1]],
75
headers, containers = client.get_account(self.url, self.token)
76
self.assertEquals(headers['x-account-container-count'], '1')
77
self.assertEquals(headers['x-account-object-count'], '1')
78
self.assertEquals(headers['x-account-bytes-used'], '3')
80
def test_two_nodes_fail(self):
82
# Kill container1 servers excepting one of the primaries
83
# Delete container1 directly to the one primary still up
84
# Restart other container1 servers
85
# Get to a final state
86
# Assert all container1 servers indicate container1 is gone (happens
87
# because the one node that knew about the delete replicated to the
89
# Assert account level also indicates container1 is gone
90
container1 = 'container-%s' % uuid4()
91
cpart, cnodes = self.container_ring.get_nodes(self.account, container1)
92
client.put_container(self.url, self.token, container1)
93
cnp_port = kill_nonprimary_server(cnodes, self.port2server, self.pids)
94
kill_server(cnodes[0]['port'], self.port2server, self.pids)
95
kill_server(cnodes[1]['port'], self.port2server, self.pids)
96
direct_client.direct_delete_container(cnodes[2], cpart, self.account,
98
start_server(cnodes[0]['port'], self.port2server, self.pids)
99
start_server(cnodes[1]['port'], self.port2server, self.pids)
100
start_server(cnp_port, self.port2server, self.pids)
105
direct_client.direct_get_container(cnode, cpart, self.account,
107
except client.ClientException, err:
109
self.assertEquals(exc.http_status, 404)
110
headers, containers = client.get_account(self.url, self.token)
111
self.assertEquals(headers['x-account-container-count'], '0')
112
self.assertEquals(headers['x-account-object-count'], '0')
113
self.assertEquals(headers['x-account-bytes-used'], '0')
337
115
def _get_container_db_files(self, container):
338
116
opart, onodes = self.container_ring.get_nodes(self.account, container)