~ubuntu-branches/ubuntu/trusty/swift/trusty-updates

« back to all changes in this revision

Viewing changes to test/probe/test_container_failures.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Soren Hansen, Chuck Short
  • Date: 2012-09-07 19:02:36 UTC
  • mfrom: (1.2.12)
  • Revision ID: package-import@ubuntu.com-20120907190236-fqrmbzm7v6zivs8d
Tags: 1.7.0-0ubuntu1
[ Soren Hansen ]
* Update debian/watch to account for symbolically named tarballs and
  use newer URL.
* Run unit tests at build time.
* Fix Launchpad URLs in debian/watch.

[ Chuck Short ]
* New upstream release
* debian/control: Add pubthon-moc as a build dep
* debian/rules: Dont fail if testsuite fails.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# See the License for the specific language governing permissions and
15
15
# limitations under the License.
16
16
 
17
 
import unittest
18
 
import os
19
 
from os import kill
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
24
 
import eventlet
25
 
import sqlite3
26
21
 
 
22
from eventlet import GreenPool, Timeout
 
23
from sqlite3 import connect
27
24
from swiftclient import client
 
25
 
28
26
from swift.common import direct_client
29
27
from swift.common.utils import hash_path, readconf
30
 
 
31
 
from test.probe.common import get_to_final_state, kill_pids, reset_environment
32
 
 
33
 
 
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
 
30
 
 
31
 
 
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)
 
37
 
 
38
 
 
39
class TestContainerFailures(TestCase):
35
40
 
36
41
    def setUp(self):
37
 
        self.pids, self.port2server, self.account_ring, self.container_ring, \
38
 
            self.object_ring, self.url, self.token, self.account = \
39
 
                reset_environment()
 
42
        (self.pids, self.port2server, self.account_ring, self.container_ring,
 
43
         self.object_ring, self.url, self.token,
 
44
         self.account) = reset_environment()
40
45
 
41
46
    def tearDown(self):
42
 
        kill_pids(self.pids)
43
 
 
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]])
49
 
 
50
 
        object1 = 'object1'
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]])
56
 
 
57
 
        cpart, cnodes = self.container_ring.get_nodes(self.account, container)
58
 
        kill(self.pids[self.port2server[cnodes[0]['port']]], SIGTERM)
59
 
 
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]])
65
 
 
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
70
 
        sleep(2)
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]])
78
 
 
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]])
86
 
 
87
 
        object2 = 'object2'
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]])
98
 
 
99
 
        get_to_final_state()
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
105
 
        # have finalized.
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]])
110
 
 
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]])
116
 
 
117
 
        object1 = 'object1'
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]])
123
 
 
124
 
        cpart, cnodes = self.container_ring.get_nodes(self.account, container)
125
 
        kill(self.pids[self.port2server[cnodes[1]['port']]], SIGTERM)
126
 
 
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]])
132
 
 
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
137
 
        sleep(2)
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]])
142
 
 
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]])
150
 
 
151
 
        object2 = 'object2'
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]])
161
 
 
162
 
        get_to_final_state()
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
168
 
        # have finalized.
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]])
173
 
 
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]])
179
 
 
180
 
        object1 = 'object1'
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]])
186
 
 
187
 
        cpart, cnodes = self.container_ring.get_nodes(self.account, container)
188
 
        for x in xrange(2):
189
 
            kill(self.pids[self.port2server[cnodes[x]['port']]], SIGTERM)
190
 
 
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]])
196
 
 
197
 
        for x in xrange(2):
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
202
 
        sleep(2)
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]])
210
 
 
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).
215
 
        exc = None
216
 
        try:
217
 
            client.delete_container(self.url, self.token, container)
218
 
        except client.ClientException, err:
219
 
            exc = err
220
 
        self.assert_(exc)
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]])
229
 
 
230
 
        object2 = 'object2'
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]])
241
 
 
242
 
        get_to_final_state()
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
248
 
        # have finalized.
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]])
253
 
 
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]])
259
 
 
260
 
        object1 = 'object1'
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]])
266
 
 
267
 
        cpart, cnodes = self.container_ring.get_nodes(self.account, container)
268
 
        for x in (1, 2):
269
 
            kill(self.pids[self.port2server[cnodes[x]['port']]], SIGTERM)
270
 
 
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]])
276
 
 
277
 
        for x in (1, 2):
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
282
 
        sleep(2)
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]])
288
 
 
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).
293
 
        exc = None
294
 
        try:
295
 
            client.delete_container(self.url, self.token, container)
296
 
        except client.ClientException, err:
297
 
            exc = err
298
 
        self.assert_(exc)
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]])
307
 
 
308
 
        object2 = 'object2'
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]])
318
 
 
319
 
        get_to_final_state()
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
325
 
        # have finalized.
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]])
330
 
 
331
 
    def _get_db_file_path(self, obj_dir):
332
 
        files = sorted(os.listdir(obj_dir), reverse=True)
333
 
        for file in files:
334
 
            if file.endswith('db'):
335
 
                return os.path.join(obj_dir, file)
 
47
        kill_servers(self.port2server, self.pids)
 
48
 
 
49
    def test_one_node_fails(self):
 
50
        # Create container1
 
51
        # Kill container1 servers excepting two of the primaries
 
52
        # Delete container1
 
53
        # Restart other container1 primary server
 
54
        # Create container1/object1 (allowed because at least server thinks the
 
55
        #   container exists)
 
56
        # Get to a final state
 
57
        # Assert all container1 servers indicate container1 is alive and
 
58
        #   well with object1
 
59
        # Assert account level also indicates container1 is alive and
 
60
        #   well with object1
 
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')
 
69
        get_to_final_state()
 
70
        for cnode in cnodes:
 
71
            self.assertEquals(
 
72
                [o['name'] for o in direct_client.direct_get_container(
 
73
                    cnode, cpart, self.account, container1)[1]],
 
74
                ['object1'])
 
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')
 
79
 
 
80
    def test_two_nodes_fail(self):
 
81
        # Create container1
 
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
 
88
        #   others.)
 
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,
 
97
                                              container1)
 
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)
 
101
        get_to_final_state()
 
102
        for cnode in cnodes:
 
103
            exc = None
 
104
            try:
 
105
                direct_client.direct_get_container(cnode, cpart, self.account,
 
106
                                                   container1)
 
107
            except client.ClientException, err:
 
108
                exc = 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')
336
114
 
337
115
    def _get_container_db_files(self, container):
338
116
        opart, onodes = self.container_ring.get_nodes(self.account, container)
343
121
            device = onode['device']
344
122
            hash_str = hash_path(self.account, container)
345
123
            server_conf = readconf('/etc/swift/container-server/%s.conf' %
346
 
                                    node_id)
 
124
                                   node_id)
347
125
            devices = server_conf['app:container-server']['devices']
348
126
            obj_dir = '%s/%s/containers/%s/%s/%s/' % (devices,
349
127
                                                      device, opart,
350
128
                                                      hash_str[-3:], hash_str)
351
 
            db_files.append(self._get_db_file_path(obj_dir))
 
129
            db_files.append(get_db_file_path(obj_dir))
352
130
 
353
131
        return db_files
354
132
 
360
138
            db_files = self._get_container_db_files(container)
361
139
            db_conns = []
362
140
            for i in range(num_locks):
363
 
                db_conn = sqlite3.connect(db_files[i])
 
141
                db_conn = connect(db_files[i])
364
142
                db_conn.execute('begin exclusive transaction')
365
143
                db_conns.append(db_conn)
366
144
            if catch_503:
 
145
                exc = None
367
146
                try:
368
147
                    client.delete_container(self.url, self.token, container)
369
 
                except client.ClientException, e:
370
 
                    self.assertEquals(e.http_status, 503)
 
148
                except client.ClientException, err:
 
149
                    exc = err
 
150
                self.assertEquals(exc.http_status, 503)
371
151
            else:
372
152
                client.delete_container(self.url, self.token, container)
373
153
 
374
 
        pool = eventlet.GreenPool()
 
154
        pool = GreenPool()
375
155
        try:
376
 
            with eventlet.Timeout(15):
377
 
                p = pool.spawn(run_test, 1, False)
378
 
                r = pool.spawn(run_test, 2, True)
379
 
                q = pool.spawn(run_test, 3, True)
 
156
            with Timeout(15):
 
157
                pool.spawn(run_test, 1, False)
 
158
                pool.spawn(run_test, 2, True)
 
159
                pool.spawn(run_test, 3, True)
380
160
                pool.waitall()
381
 
        except eventlet.Timeout, e:
 
161
        except Timeout, err:
382
162
            raise Exception(
383
163
                "The server did not return a 503 on container db locks, "
384
 
                "it just hangs: %s" % e)
 
164
                "it just hangs: %s" % err)
385
165
 
386
166
 
387
167
if __name__ == '__main__':
388
 
    unittest.main()
 
168
    main()