Merge "Fix probe failure and small things"

This commit is contained in:
Jenkins 2016-05-19 23:13:06 +00:00 committed by Gerrit Code Review
commit b983830a6a
2 changed files with 86 additions and 26 deletions

View File

@ -142,11 +142,16 @@ class BrainSplitter(object):
""" """
put container with next storage policy put container with next storage policy
""" """
policy = next(self.policies)
if policy_index is not None: if policy_index is not None:
policy = POLICIES.get_by_index(int(policy_index)) policy = POLICIES.get_by_index(int(policy_index))
if not policy: if not policy:
raise ValueError('Unknown policy with index %s' % policy) raise ValueError('Unknown policy with index %s' % policy)
elif not self.policy:
policy = next(self.policies)
else:
policy = self.policy
headers = {'X-Storage-Policy': policy.name} headers = {'X-Storage-Policy': policy.name}
client.put_container(self.url, self.token, self.container_name, client.put_container(self.url, self.token, self.container_name,
headers=headers) headers=headers)

View File

@ -19,7 +19,7 @@ import unittest
from nose import SkipTest from nose import SkipTest
from swift.common.internal_client import InternalClient from swift.common.internal_client import InternalClient, UnexpectedResponse
from swift.common.manager import Manager from swift.common.manager import Manager
from swift.common.utils import Timestamp from swift.common.utils import Timestamp
@ -32,9 +32,6 @@ from swiftclient import client
class TestObjectExpirer(ReplProbeTest): class TestObjectExpirer(ReplProbeTest):
def setUp(self): def setUp(self):
if len(ENABLED_POLICIES) < 2:
raise SkipTest('Need more than one policy')
self.expirer = Manager(['object-expirer']) self.expirer = Manager(['object-expirer'])
self.expirer.start() self.expirer.start()
err = self.expirer.stop() err = self.expirer.stop()
@ -54,6 +51,9 @@ class TestObjectExpirer(ReplProbeTest):
self.object_name) self.object_name)
def test_expirer_object_split_brain(self): def test_expirer_object_split_brain(self):
if len(ENABLED_POLICIES) < 2:
raise SkipTest('Need more than one policy')
old_policy = random.choice(ENABLED_POLICIES) old_policy = random.choice(ENABLED_POLICIES)
wrong_policy = random.choice([p for p in ENABLED_POLICIES wrong_policy = random.choice([p for p in ENABLED_POLICIES
if p != old_policy]) if p != old_policy])
@ -128,6 +128,32 @@ class TestObjectExpirer(ReplProbeTest):
create_timestamp) create_timestamp)
def test_expirer_object_should_not_be_expired(self): def test_expirer_object_should_not_be_expired(self):
# Current object-expirer checks the correctness via x-if-delete-at
# header that it can be deleted by expirer. If there are objects
# either which doesn't have x-delete-at header as metadata or which
# has different x-delete-at value from x-if-delete-at value,
# object-expirer's delete will fail as 412 PreconditionFailed.
# However, if some of the objects are in handoff nodes, the expirer
# can put the tombstone with the timestamp as same as x-delete-at and
# the object consistency will be resolved as the newer timestamp will
# be winner (in particular, overwritten case w/o x-delete-at). This
# test asserts such a situation that, at least, the overwriten object
# which have larger timestamp than the original expirered date should
# be safe.
def put_object(headers):
# use internal client to PUT objects so that X-Timestamp in headers
# is effective
headers['Content-Length'] = '0'
path = self.client.make_path(
self.account, self.container_name, self.object_name)
try:
self.client.make_request('PUT', path, headers, (2,))
except UnexpectedResponse as e:
self.fail(
'Expected 201 for PUT object but got %s' % e.resp.status)
obj_brain = BrainSplitter(self.url, self.token, self.container_name, obj_brain = BrainSplitter(self.url, self.token, self.container_name,
self.object_name, 'object', self.policy) self.object_name, 'object', self.policy)
@ -135,40 +161,69 @@ class TestObjectExpirer(ReplProbeTest):
# < T(expirer_executed) # < T(expirer_executed)
# Recreated obj should be appeared in any split brain case # Recreated obj should be appeared in any split brain case
# T(obj_created) obj_brain.put_container()
first_created_at = time.time()
# T(obj_deleted with x-delete-at) # T(obj_deleted with x-delete-at)
# object-server accepts req only if X-Delete-At is later than 'now' # object-server accepts req only if X-Delete-At is later than 'now'
delete_at = int(time.time() + 1.5) # so here, T(obj_created) < T(obj_deleted with x-delete-at)
# T(obj_recreated) now = time.time()
recreated_at = time.time() + 2.0 delete_at = int(now + 2.0)
# T(expirer_executed) - 'now' recreate_at = delete_at + 1.0
sleep_for_expirer = 2.01 put_object(headers={'X-Delete-At': delete_at,
'X-Timestamp': Timestamp(now).normal})
obj_brain.put_container(int(self.policy)) # some object servers stopped to make a situation that the
obj_brain.put_object( # object-expirer can put tombstone in the primary nodes.
headers={'X-Delete-At': delete_at,
'X-Timestamp': Timestamp(first_created_at).internal})
# some object servers stopped
obj_brain.stop_primary_half() obj_brain.stop_primary_half()
obj_brain.put_object(
headers={'X-Timestamp': Timestamp(recreated_at).internal, # increment the X-Timestamp explicitly
'X-Object-Meta-Expired': 'False'}) # (will be T(obj_deleted with x-delete-at) < T(obj_recreated))
put_object(headers={'X-Object-Meta-Expired': 'False',
'X-Timestamp': Timestamp(recreate_at).normal})
# make sure auto-created containers get in the account listing # make sure auto-created containers get in the account listing
Manager(['container-updater']).once() Manager(['container-updater']).once()
# sanity, the newer object is still there
try:
metadata = self.client.get_object_metadata(
self.account, self.container_name, self.object_name)
except UnexpectedResponse as e:
self.fail(
'Expected 200 for HEAD object but got %s' % e.resp.status)
self.assertIn('x-object-meta-expired', metadata)
# some object servers recovered # some object servers recovered
obj_brain.start_primary_half() obj_brain.start_primary_half()
# sleep to make sure expirer runs at the time after obj is recreated
time.sleep(sleep_for_expirer) # sleep until after recreated_at
while time.time() <= recreate_at:
time.sleep(0.1)
# Now, expirer runs at the time after obj is recreated
self.expirer.once() self.expirer.once()
# inconsistent state of objects is recovered
# verify that original object was deleted by expirer
obj_brain.stop_handoff_half()
try:
metadata = self.client.get_object_metadata(
self.account, self.container_name, self.object_name,
acceptable_statuses=(4,))
except UnexpectedResponse as e:
self.fail(
'Expected 404 for HEAD object but got %s' % e.resp.status)
obj_brain.start_handoff_half()
# and inconsistent state of objects is recovered by replicator
Manager(['object-replicator']).once() Manager(['object-replicator']).once()
# check if you can get recreated object # check if you can get recreated object
metadata = self.client.get_object_metadata( try:
self.account, self.container_name, self.object_name) metadata = self.client.get_object_metadata(
self.account, self.container_name, self.object_name)
except UnexpectedResponse as e:
self.fail(
'Expected 200 for HEAD object but got %s' % e.resp.status)
self.assertIn('x-object-meta-expired', metadata) self.assertIn('x-object-meta-expired', metadata)