Merge "in case you lose your builder backups"

This commit is contained in:
Jenkins 2013-11-28 01:24:46 +00:00 committed by Gerrit Code Review
commit 438596fc0f
3 changed files with 90 additions and 11 deletions

View File

@ -25,11 +25,11 @@ from textwrap import wrap
from time import time from time import time
from swift.common import exceptions from swift.common import exceptions
from swift.common.ring import RingBuilder from swift.common.ring import RingBuilder, Ring
from swift.common.ring.builder import MAX_BALANCE from swift.common.ring.builder import MAX_BALANCE
from swift.common.utils import lock_parent_directory from swift.common.utils import lock_parent_directory
from swift.common.ring.utils import parse_search_value, parse_args, \ from swift.common.ring.utils import parse_search_value, parse_args, \
build_dev_from_opts build_dev_from_opts, parse_builder_ring_filename_args
MAJOR_VERSION = 1 MAJOR_VERSION = 1
MINOR_VERSION = 3 MINOR_VERSION = 3
@ -697,6 +697,52 @@ swift-ring-builder <builder_file> write_ring
ring_data.save(ring_file) ring_data.save(ring_file)
exit(EXIT_SUCCESS) exit(EXIT_SUCCESS)
def write_builder():
"""
swift-ring-builder <ring_file> write_builder [min_part_hours]
Recreate a builder from a ring file (lossy) if you lost your builder
backups. (Protip: don't lose your builder backups).
[min_part_hours] is one of those numbers lost to the builder,
you can change it with set_min_part_hours.
"""
if exists(builder_file):
print 'Cowardly refusing to overwrite existing ' \
'Ring Builder file: %s' % builder_file
exit(EXIT_ERROR)
if len(argv) > 3:
min_part_hours = int(argv[3])
else:
stderr.write("WARNING: default min_part_hours may not match "
"the value in the lost builder.\n")
min_part_hours = 24
ring = Ring(ring_file)
for dev in ring.devs:
dev.update({
'parts': 0,
'parts_wanted': 0,
})
builder_dict = {
'part_power': 32 - ring._part_shift,
'replicas': float(ring.replica_count),
'min_part_hours': min_part_hours,
'parts': ring.partition_count,
'devs': ring.devs,
'devs_changed': False,
'version': 0,
'_replica2part2dev': ring._replica2part2dev_id,
'_last_part_moves_epoch': None,
'_last_part_moves': None,
'_last_part_gather_start': 0,
'_remove_devs': [],
}
builder = RingBuilder(1, 1, 1)
builder.copy_from(builder_dict)
for parts in builder._replica2part2dev:
for dev_id in parts:
builder.devs[dev_id]['parts'] += 1
builder._set_parts_wanted()
builder.save(builder_file)
def pretend_min_part_hours_passed(): def pretend_min_part_hours_passed():
builder.pretend_min_part_hours_passed() builder.pretend_min_part_hours_passed()
builder.save(argv[1]) builder.save(argv[1])
@ -772,9 +818,11 @@ if __name__ == '__main__':
' 2 = error') ' 2 = error')
exit(EXIT_SUCCESS) exit(EXIT_SUCCESS)
if exists(argv[1]): builder_file, ring_file = parse_builder_ring_filename_args(argv)
builder = RingBuilder.load(argv[1])
elif len(argv) < 3 or argv[2] != 'create': if exists(builder_file):
builder = RingBuilder.load(builder_file)
elif len(argv) < 3 or argv[2] not in('create', 'write_builder'):
print 'Ring Builder file does not exist: %s' % argv[1] print 'Ring Builder file does not exist: %s' % argv[1]
exit(EXIT_ERROR) exit(EXIT_ERROR)
@ -785,11 +833,6 @@ if __name__ == '__main__':
if err.errno != EEXIST: if err.errno != EEXIST:
raise raise
ring_file = argv[1]
if ring_file.endswith('.builder'):
ring_file = ring_file[:-len('.builder')]
ring_file += '.ring.gz'
if len(argv) == 2: if len(argv) == 2:
command = "default" command = "default"
else: else:

View File

@ -269,6 +269,22 @@ def parse_args(argvish):
return parser.parse_args(argvish) return parser.parse_args(argvish)
def parse_builder_ring_filename_args(argvish):
first_arg = argvish[1]
if first_arg.endswith('.ring.gz'):
ring_file = first_arg
builder_file = first_arg[:-len('.ring.gz')] + '.builder'
else:
builder_file = first_arg
if not builder_file.endswith('.builder'):
ring_file = first_arg
else:
ring_file = builder_file[:-len('.builder')]
if not first_arg.endswith('.ring.gz'):
ring_file += '.ring.gz'
return builder_file, ring_file
def build_dev_from_opts(opts): def build_dev_from_opts(opts):
""" """
Convert optparse stype options into a device dictionary. Convert optparse stype options into a device dictionary.

View File

@ -17,7 +17,8 @@ import unittest
from swift.common.ring.utils import (build_tier_tree, tiers_for_dev, from swift.common.ring.utils import (build_tier_tree, tiers_for_dev,
parse_search_value, parse_args, parse_search_value, parse_args,
build_dev_from_opts) build_dev_from_opts,
parse_builder_ring_filename_args)
class TestUtils(unittest.TestCase): class TestUtils(unittest.TestCase):
@ -139,6 +140,25 @@ class TestUtils(unittest.TestCase):
} }
self.assertEquals(device, expected) self.assertEquals(device, expected)
def test_parse_builder_ring_filename_args(self):
args = 'swift-ring-builder object.builder write_ring'
self.assertEquals((
'object.builder', 'object.ring.gz'
), parse_builder_ring_filename_args(args.split()))
args = 'swift-ring-builder container.ring.gz write_builder'
self.assertEquals((
'container.builder', 'container.ring.gz'
), parse_builder_ring_filename_args(args.split()))
# builer name arg should always fall through
args = 'swift-ring-builder test create'
self.assertEquals((
'test', 'test.ring.gz'
), parse_builder_ring_filename_args(args.split()))
args = 'swift-ring-builder my.file.name create'
self.assertEquals((
'my.file.name', 'my.file.name.ring.gz'
), parse_builder_ring_filename_args(args.split()))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()