in case you lose your builder backups

Change-Id: Ica555be2be492c3ec5fdeab738058ff35989a603
This commit is contained in:
Clay Gerrard 2013-10-29 13:04:59 -07:00
parent 9081b33048
commit 37e0654adb
3 changed files with 90 additions and 11 deletions

View File

@ -25,11 +25,11 @@ from textwrap import wrap
from time import time
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.utils import lock_parent_directory
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
MINOR_VERSION = 3
@ -697,6 +697,52 @@ swift-ring-builder <builder_file> write_ring
ring_data.save(ring_file)
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():
builder.pretend_min_part_hours_passed()
builder.save(argv[1])
@ -772,9 +818,11 @@ if __name__ == '__main__':
' 2 = error')
exit(EXIT_SUCCESS)
if exists(argv[1]):
builder = RingBuilder.load(argv[1])
elif len(argv) < 3 or argv[2] != 'create':
builder_file, ring_file = parse_builder_ring_filename_args(argv)
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]
exit(EXIT_ERROR)
@ -785,11 +833,6 @@ if __name__ == '__main__':
if err.errno != EEXIST:
raise
ring_file = argv[1]
if ring_file.endswith('.builder'):
ring_file = ring_file[:-len('.builder')]
ring_file += '.ring.gz'
if len(argv) == 2:
command = "default"
else:

View File

@ -269,6 +269,22 @@ def 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):
"""
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,
parse_search_value, parse_args,
build_dev_from_opts)
build_dev_from_opts,
parse_builder_ring_filename_args)
class TestUtils(unittest.TestCase):
@ -139,6 +140,25 @@ class TestUtils(unittest.TestCase):
}
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__':
unittest.main()