swift/test/unit/cli/test_ringbuilder.py
Clay Gerrard 5070869ac0 Validate against duplicate device part replica assignment
We should never assign multiple replicas of the same partition to the
same device - our on-disk layout can only support a single replica of a
given part on a single device.  We should not do this, so we validate
against it and raise a loud warning if this terrible state is ever
observed after a rebalance.

Unfortunately currently there's a couple not necessarily uncommon
scenarios which will trigger this observed state today:

 1. If we have less devices than replicas
 2. If a server or zones aggregate device weight make it the most
    appropriate candidate for multiple replicas and you're a bit unlucky

Fixing #1 would be easy, we should just not allow that state anymore.
Really we never did - if you have a 3 replica ring with one device - you
have one replica.  Everything that iter_nodes'd would de-dupe.  We
should just be insisting that you explicitly acknowledge your replica
count with set_replicas.

I have been lost in the abyss for days searching for a general solutions
to #2.  I'm sure it exists, but I will not have wrestled it to
submission by RC1.  In the meantime we can eliminate a great deal of the
luck required simply by refusing to place more than one replica of a
part on a device in assign_parts.

The meat of the change is a small update to the .validate method in
RingBuilder.  It basically unrolls a pre-existing (part, replica) loop
so that all the replicas of the part come out in order so that we can
build up the set of dev_id's for which all the replicas of a given part
are assigned part-by-part.

If we observe any duplicates - we raise a warning.

To clean the cobwebs out of the rest of the corner cases we're going to
delay get_required_overload from kicking in until we achive dispersion,
and a small check was added when selecting a device subtier to validate
if it's already being used - picking any other device in the tier works
out much better.  If no other devices are available in the tier - we
raise a warning.  A more elegant or optimized solution may exist.

Many unittests did not meet the criteria #1, but the fix was straight
forward after being identified by the pigeonhole check.

However, many more tests were affected by #2 - but again the fix came to
be simply adding more devices.  The fantasy that all failure domains
contain at least replica count devices is prevalent in both our ring
placement algorithm and it's tests.  These tests were trying to
demonstrate some complex characteristics of our ring placement algorithm
and I believe we just got a bit too carried away trying to find the
simplest possible example to demonstrate the desirable trait.  I think
a better example looks more like a real ring - with many devices in each
server and many servers in each zone - I think more devices makes the
tests better.  As much as possible I've tried to maintain the original
intent of the tests - when adding devices I've either spread the weight
out amongst them or added proportional weights to the other tiers.

I added an example straw man test to validate that three devices with
different weights in three different zones won't blow up.  Once we can
do that without raising warnings and assigning duplicate device part
replicas - we can add more.  And more importantly change the warnings to
errors - because we would much prefer to not do that #$%^ anymore.

Co-Authored-By: Kota Tsuyuzaki <tsuyuzaki.kota@lab.ntt.co.jp>
Related-Bug: #1452431
Change-Id: I592d5b611188670ae842fe3d030aa3b340ac36f9
2015-10-02 16:42:25 -07:00

1943 lines
72 KiB
Python

# Copyright (c) 2014 Christian Schwede <christian.schwede@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import mock
import os
import six
import tempfile
import unittest
import uuid
import shlex
from swift.cli import ringbuilder
from swift.common import exceptions
from swift.common.ring import RingBuilder
class RunSwiftRingBuilderMixin(object):
def run_srb(self, *argv):
if len(argv) == 1 and isinstance(argv[0], basestring):
# convert a single string to a list
argv = shlex.split(argv[0])
mock_stdout = six.StringIO()
mock_stderr = six.StringIO()
srb_args = ["", self.tempfile] + [str(s) for s in argv]
try:
with mock.patch("sys.stdout", mock_stdout):
with mock.patch("sys.stderr", mock_stderr):
ringbuilder.main(srb_args)
except SystemExit as err:
if err.code not in (0, 1): # (success, warning)
msg = 'Unexpected exit status %s\n' % err.code
msg += 'STDOUT:\n%s\nSTDERR:\n%s\n' % (
mock_stdout.getvalue(), mock_stderr.getvalue())
self.fail(msg)
return (mock_stdout.getvalue(), mock_stderr.getvalue())
class TestCommands(unittest.TestCase, RunSwiftRingBuilderMixin):
def __init__(self, *args, **kwargs):
super(TestCommands, self).__init__(*args, **kwargs)
# List of search values for various actions
# These should all match the first device in the sample ring
# (see below) but not the second device
self.search_values = ["d0", "/sda1", "r0", "z0", "z0-127.0.0.1",
"127.0.0.1", "z0:6000", ":6000", "R127.0.0.1",
"127.0.0.1R127.0.0.1", "R:6000",
"_some meta data"]
tmpf = tempfile.NamedTemporaryFile()
self.tempfile = self.tmpfile = tmpf.name
def tearDown(self):
try:
os.remove(self.tmpfile)
except OSError:
pass
def create_sample_ring(self, part_power=6):
""" Create a sample ring with four devices
At least four devices are needed to test removing
a device, since having less devices than replicas
is not allowed.
"""
# Ensure there is no existing test builder file because
# create_sample_ring() might be used more than once in a single test
try:
os.remove(self.tmpfile)
except OSError:
pass
ring = RingBuilder(part_power, 3, 1)
ring.add_dev({'weight': 100.0,
'region': 0,
'zone': 0,
'ip': '127.0.0.1',
'port': 6000,
'device': 'sda1',
'meta': 'some meta data',
})
ring.add_dev({'weight': 100.0,
'region': 1,
'zone': 1,
'ip': '127.0.0.2',
'port': 6001,
'device': 'sda2'
})
ring.add_dev({'weight': 100.0,
'region': 2,
'zone': 2,
'ip': '127.0.0.3',
'port': 6002,
'device': 'sdc3'
})
ring.add_dev({'weight': 100.0,
'region': 3,
'zone': 3,
'ip': '127.0.0.4',
'port': 6003,
'device': 'sdd4'
})
ring.save(self.tmpfile)
def test_parse_search_values_old_format(self):
# Test old format
argv = ["d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data"]
search_values = ringbuilder._parse_search_values(argv)
self.assertEqual(search_values['id'], 0)
self.assertEqual(search_values['region'], 0)
self.assertEqual(search_values['zone'], 0)
self.assertEqual(search_values['ip'], '127.0.0.1')
self.assertEqual(search_values['port'], 6000)
self.assertEqual(search_values['replication_ip'], '127.0.0.1')
self.assertEqual(search_values['replication_port'], 6000)
self.assertEqual(search_values['device'], 'sda1')
self.assertEqual(search_values['meta'], 'some meta data')
def test_parse_search_values_new_format(self):
# Test new format
argv = ["--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data",
"--weight", "100"]
search_values = ringbuilder._parse_search_values(argv)
self.assertEqual(search_values['id'], 0)
self.assertEqual(search_values['region'], 0)
self.assertEqual(search_values['zone'], 0)
self.assertEqual(search_values['ip'], '127.0.0.1')
self.assertEqual(search_values['port'], 6000)
self.assertEqual(search_values['replication_ip'], '127.0.0.1')
self.assertEqual(search_values['replication_port'], 6000)
self.assertEqual(search_values['device'], 'sda1')
self.assertEqual(search_values['meta'], 'some meta data')
self.assertEqual(search_values['weight'], 100)
def test_parse_search_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["--region", "2", "test"]
err = None
try:
ringbuilder._parse_search_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_find_parts(self):
rb = RingBuilder(8, 3, 0)
rb.add_dev({'id': 0, 'region': 1, 'zone': 0, 'weight': 100,
'ip': '127.0.0.1', 'port': 10000, 'device': 'sda1'})
rb.add_dev({'id': 3, 'region': 1, 'zone': 0, 'weight': 100,
'ip': '127.0.0.1', 'port': 10000, 'device': 'sdb1'})
rb.add_dev({'id': 1, 'region': 1, 'zone': 1, 'weight': 100,
'ip': '127.0.0.1', 'port': 10001, 'device': 'sda1'})
rb.add_dev({'id': 4, 'region': 1, 'zone': 1, 'weight': 100,
'ip': '127.0.0.1', 'port': 10001, 'device': 'sdb1'})
rb.add_dev({'id': 2, 'region': 1, 'zone': 2, 'weight': 100,
'ip': '127.0.0.1', 'port': 10002, 'device': 'sda1'})
rb.add_dev({'id': 5, 'region': 1, 'zone': 2, 'weight': 100,
'ip': '127.0.0.1', 'port': 10002, 'device': 'sdb1'})
rb.rebalance()
rb.add_dev({'id': 6, 'region': 2, 'zone': 1, 'weight': 10,
'ip': '127.0.0.1', 'port': 10004, 'device': 'sda1'})
rb.pretend_min_part_hours_passed()
rb.rebalance()
ringbuilder.builder = rb
sorted_partition_count = ringbuilder._find_parts(
rb.search_devs({'ip': '127.0.0.1'}))
# Expect 256 partitions in the output
self.assertEqual(256, len(sorted_partition_count))
# Each partitions should have 3 replicas
for partition, count in sorted_partition_count:
self.assertEqual(
3, count, "Partition %d has only %d replicas" %
(partition, count))
def test_parse_list_parts_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["--region", "2", "test"]
err = None
try:
ringbuilder._parse_list_parts_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_parse_add_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["--region", "2", "test"]
err = None
try:
ringbuilder._parse_add_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_weight_values_no_devices(self):
# Test no devices
err = None
try:
ringbuilder._set_weight_values([], 100)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_parse_set_weight_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["r1", "100", "r2"]
err = None
try:
ringbuilder._parse_set_weight_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
argv = ["--region", "2"]
err = None
try:
ringbuilder._parse_set_weight_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_info_values_no_devices(self):
# Test no devices
err = None
try:
ringbuilder._set_info_values([], 100)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_parse_set_info_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["r1", "127.0.0.1", "r2"]
err = None
try:
ringbuilder._parse_set_info_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_parse_remove_values_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["--region", "2", "test"]
err = None
try:
ringbuilder._parse_remove_values(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_create_ring(self):
argv = ["", self.tmpfile, "create", "6", "3.14159265359", "1"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.part_power, 6)
self.assertEqual(ring.replicas, 3.14159265359)
self.assertEqual(ring.min_part_hours, 1)
def test_add_device_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "add",
"r2z3-127.0.0.1:6000/sda3_some meta data", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 3.14159265359)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['meta'], 'some meta data')
def test_add_device_ipv6_old_format(self):
self.create_sample_ring()
# Test ipv6(old format)
argv = \
["", self.tmpfile, "add",
"r2z3-2001:0000:1234:0000:0000:C1C0:ABCD:0876:6000"
"R2::10:7000/sda3_some meta data",
"3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '2001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 3.14159265359)
self.assertEqual(dev['replication_ip'], '2::10')
self.assertEqual(dev['replication_port'], 7000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_add_device_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "127.0.0.2",
"--port", "6000",
"--replication-ip", "127.0.0.2",
"--replication-port", "6000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 3.14159265359)
self.assertEqual(dev['replication_ip'], '127.0.0.2')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_add_device_ipv6_new_format(self):
self.create_sample_ring()
# Test ipv6(new format)
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[3001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[3::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '3001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 3.14159265359)
self.assertEqual(dev['replication_ip'], '3::10')
self.assertEqual(dev['replication_port'], 7000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_add_device_domain_new_format(self):
self.create_sample_ring()
# Test domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], 'test.test.com')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 3.14159265359)
self.assertEqual(dev['replication_ip'], 'r.test.com')
self.assertEqual(dev['replication_port'], 7000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_add_device_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "add"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_add_device_already_exists(self):
# Test Add a device that already exists
argv = ["", self.tmpfile, "add",
"r0z0-127.0.0.1:6000/sda1_some meta data", "100"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_remove_device(self):
for search_value in self.search_values:
self.create_sample_ring()
argv = ["", self.tmpfile, "remove", search_value]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was set to 0
dev = ring.devs[0]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 0)
self.assertEqual(dev['zone'], 0)
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "remove",
"d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was set to 0
dev = ring.devs[0]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 0)
self.assertEqual(dev['zone'], 0)
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_ipv6_old_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(old format)
argv = ["", self.tmpfile, "remove",
"d4r2z3-[2001:0000:1234:0000:0000:C1C0:ABCD:0876]:6000"
"R[2::10]:7000/sda3_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 0])
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Check that weight was set to 0
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '2001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], '2::10')
self.assertEqual(dev['replication_port'], 7000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "remove",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was set to 0
dev = ring.devs[0]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 0)
self.assertEqual(dev['zone'], 0)
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_ipv6_new_format(self):
self.create_sample_ring()
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[3001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "8000",
"--replication-ip", "[3::10]",
"--replication-port", "9000",
"--device", "sda30", "--meta", "other meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(new format)
argv = \
["", self.tmpfile, "remove",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "[3001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "8000",
"--replication-ip", "[3::10]",
"--replication-port", "9000",
"--device", "sda30", "--meta", "other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 0])
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Check that weight was set to 0
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], '3001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 8000)
self.assertEqual(dev['device'], 'sda30')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], '3::10')
self.assertEqual(dev['replication_port'], 9000)
self.assertEqual(dev['meta'], 'other meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_domain_new_format(self):
self.create_sample_ring()
# add domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test domain name
argv = \
["", self.tmpfile, "remove",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 0])
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
self.assertFalse([d for d in ring._remove_devs if d['id'] == 1])
# Check that weight was set to 0
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
self.assertEqual(dev['region'], 2)
self.assertEqual(dev['zone'], 3)
self.assertEqual(dev['ip'], 'test.test.com')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['device'], 'sda3')
self.assertEqual(dev['weight'], 0)
self.assertEqual(dev['replication_ip'], 'r.test.com')
self.assertEqual(dev['replication_port'], 7000)
self.assertEqual(dev['meta'], 'some meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_remove_device_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "remove"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_remove_device_no_matching(self):
self.create_sample_ring()
# Test No matching devices
argv = ["", self.tmpfile, "remove",
"--ip", "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_weight(self):
for search_value in self.search_values:
self.create_sample_ring()
argv = ["", self.tmpfile, "set_weight",
search_value, "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was changed
dev = ring.devs[0]
self.assertEqual(dev['weight'], 3.14159265359)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "set_weight",
"d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data",
"3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was changed
dev = ring.devs[0]
self.assertEqual(dev['weight'], 3.14159265359)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_ipv6_old_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "100"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(old format)
argv = ["", self.tmpfile, "set_weight",
"d4r2z3-[2001:0000:1234:0000:0000:C1C0:ABCD:0876]:6000"
"R[2::10]:7000/sda3_some meta data", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Check that weight was changed
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 3.14159265359)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "set_weight",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was changed
dev = ring.devs[0]
self.assertEqual(dev['weight'], 3.14159265359)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_ipv6_new_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "100"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(new format)
argv = \
["", self.tmpfile, "set_weight",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Check that weight was changed
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 3.14159265359)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_domain_new_format(self):
self.create_sample_ring()
# add domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "100"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test domain name
argv = \
["", self.tmpfile, "set_weight",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['weight'], 100)
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['weight'], 100)
# Check that weight was changed
dev = ring.devs[-1]
self.assertEqual(dev['weight'], 3.14159265359)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_weight_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "set_weight"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_weight_no_matching(self):
self.create_sample_ring()
# Test No matching devices
argv = ["", self.tmpfile, "set_weight",
"--ip", "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_info(self):
for search_value in self.search_values:
self.create_sample_ring()
argv = ["", self.tmpfile, "set_info", search_value,
"127.0.1.1:8000/sda1_other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.1.1')
self.assertEqual(dev['port'], 8000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['meta'], 'other meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "set_info",
"d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data",
"127.0.1.1:8000R127.0.1.1:8000/sda10_other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.1.1')
self.assertEqual(dev['port'], 8000)
self.assertEqual(dev['replication_ip'], '127.0.1.1')
self.assertEqual(dev['replication_port'], 8000)
self.assertEqual(dev['device'], 'sda10')
self.assertEqual(dev['meta'], 'other meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_ipv6_old_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(old format)
argv = ["", self.tmpfile, "set_info",
"d4r2z3-[2001:0000:1234:0000:0000:C1C0:ABCD:0876]:6000"
"R[2::10]:7000/sda3_some meta data",
"[3001:0000:1234:0000:0000:C1C0:ABCD:0876]:8000"
"R[3::10]:8000/sda30_other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Check that device was created with given data
dev = ring.devs[-1]
self.assertEqual(dev['ip'], '3001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 8000)
self.assertEqual(dev['replication_ip'], '3::10')
self.assertEqual(dev['replication_port'], 8000)
self.assertEqual(dev['device'], 'sda30')
self.assertEqual(dev['meta'], 'other meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "set_info",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data",
"--change-ip", "127.0.2.1",
"--change-port", "9000",
"--change-replication-ip", "127.0.2.1",
"--change-replication-port", "9000",
"--change-device", "sda100", "--change-meta", "other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.2.1')
self.assertEqual(dev['port'], 9000)
self.assertEqual(dev['replication_ip'], '127.0.2.1')
self.assertEqual(dev['replication_port'], 9000)
self.assertEqual(dev['device'], 'sda100')
self.assertEqual(dev['meta'], 'other meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_ipv6_new_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(new format)
argv = \
["", self.tmpfile, "set_info",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--change-ip", "[4001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--change-port", "9000",
"--change-replication-ip", "[4::10]",
"--change-replication-port", "9000",
"--change-device", "sda300", "--change-meta", "other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = ring.devs[-1]
self.assertEqual(dev['ip'], '4001:0:1234::c1c0:abcd:876')
self.assertEqual(dev['port'], 9000)
self.assertEqual(dev['replication_ip'], '4::10')
self.assertEqual(dev['replication_port'], 9000)
self.assertEqual(dev['device'], 'sda300')
self.assertEqual(dev['meta'], 'other meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_domain_new_format(self):
self.create_sample_ring()
# add domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test domain name
argv = \
["", self.tmpfile, "set_info",
"--id", "4", "--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--change-ip", "test.test2.com",
"--change-port", "9000",
"--change-replication-ip", "r.test2.com",
"--change-replication-port", "9000",
"--change-device", "sda300", "--change-meta", "other meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that second device in ring is not affected
dev = ring.devs[0]
self.assertEqual(dev['ip'], '127.0.0.1')
self.assertEqual(dev['port'], 6000)
self.assertEqual(dev['replication_ip'], '127.0.0.1')
self.assertEqual(dev['replication_port'], 6000)
self.assertEqual(dev['device'], 'sda1')
self.assertEqual(dev['meta'], 'some meta data')
# Check that second device in ring is not affected
dev = ring.devs[1]
self.assertEqual(dev['ip'], '127.0.0.2')
self.assertEqual(dev['port'], 6001)
self.assertEqual(dev['device'], 'sda2')
self.assertEqual(dev['meta'], '')
# Check that device was created with given data
dev = ring.devs[-1]
self.assertEqual(dev['ip'], 'test.test2.com')
self.assertEqual(dev['port'], 9000)
self.assertEqual(dev['replication_ip'], 'r.test2.com')
self.assertEqual(dev['replication_port'], 9000)
self.assertEqual(dev['device'], 'sda300')
self.assertEqual(dev['meta'], 'other meta data')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
def test_set_info_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "set_info"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_info_no_matching(self):
self.create_sample_ring()
# Test No matching devices
argv = ["", self.tmpfile, "set_info",
"--ip", "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_info_already_exists(self):
self.create_sample_ring()
# Test Set a device that already exists
argv = \
["", self.tmpfile, "set_info",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data",
"--change-ip", "127.0.0.2",
"--change-port", "6001",
"--change-replication-ip", "127.0.0.2",
"--change-replication-port", "6001",
"--change-device", "sda2", "--change-meta", ""]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_min_part_hours(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_min_part_hours", "24"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.min_part_hours, 24)
def test_set_min_part_hours_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "set_min_part_hours"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_replicas(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_replicas", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.replicas, 3.14159265359)
def test_set_overload(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_overload", "0.19878"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 0.19878)
def test_set_overload_negative(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_overload", "-0.19878"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 0.0)
def test_set_overload_non_numeric(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_overload", "swedish fish"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 0.0)
def test_set_overload_percent(self):
self.create_sample_ring()
argv = "set_overload 10%".split()
out, err = self.run_srb(*argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 0.1)
self.assertTrue('10.00%' in out)
self.assertTrue('0.100000' in out)
def test_set_overload_percent_strange_input(self):
self.create_sample_ring()
argv = "set_overload 26%%%%".split()
out, err = self.run_srb(*argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 0.26)
self.assertTrue('26.00%' in out)
self.assertTrue('0.260000' in out)
def test_server_overload_crazy_high(self):
self.create_sample_ring()
argv = "set_overload 10".split()
out, err = self.run_srb(*argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 10.0)
self.assertTrue('Warning overload is greater than 100%' in out)
self.assertTrue('1000.00%' in out)
self.assertTrue('10.000000' in out)
# but it's cool if you do it on purpose
argv[-1] = '1000%'
out, err = self.run_srb(*argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.overload, 10.0)
self.assertTrue('Warning overload is greater than 100%' not in out)
self.assertTrue('1000.00%' in out)
self.assertTrue('10.000000' in out)
def test_set_replicas_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "set_replicas"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_set_replicas_invalid_value(self):
# Test not a valid number
argv = ["", self.tmpfile, "set_replicas", "test"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
# Test new replicas is 0
argv = ["", self.tmpfile, "set_replicas", "0"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_validate(self):
self.create_sample_ring()
ring = RingBuilder.load(self.tmpfile)
ring.rebalance()
ring.save(self.tmpfile)
argv = ["", self.tmpfile, "validate"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_validate_empty_file(self):
open(self.tmpfile, 'a').close
argv = ["", self.tmpfile, "validate"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_validate_corrupted_file(self):
self.create_sample_ring()
ring = RingBuilder.load(self.tmpfile)
ring.rebalance()
self.assertTrue(ring.validate()) # ring is valid until now
ring.save(self.tmpfile)
argv = ["", self.tmpfile, "validate"]
# corrupt the file
with open(self.tmpfile, 'wb') as f:
f.write(os.urandom(1024))
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_validate_non_existent_file(self):
rand_file = '%s/%s' % ('/tmp', str(uuid.uuid4()))
argv = ["", rand_file, "validate"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_validate_non_accessible_file(self):
with mock.patch.object(
RingBuilder, 'load',
mock.Mock(side_effect=exceptions.PermissionError)):
argv = ["", self.tmpfile, "validate"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_validate_generic_error(self):
with mock.patch.object(
RingBuilder, 'load', mock.Mock(
side_effect=IOError('Generic error occurred'))):
argv = ["", self.tmpfile, "validate"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_search_device_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "search",
"d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_search_device_ipv6_old_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(old format)
argv = ["", self.tmpfile, "search",
"d2r2z3-[2001:0000:1234:0000:0000:C1C0:ABCD:0876]:6000"
"R[2::10]:7000/sda3_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_search_device_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "search",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_search_device_ipv6_new_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(new format)
argv = \
["", self.tmpfile, "search",
"--id", "2", "--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_search_device_domain_new_format(self):
self.create_sample_ring()
# add domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test domain name
argv = \
["", self.tmpfile, "search",
"--id", "2", "--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_search_device_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "search"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_search_device_no_matching(self):
self.create_sample_ring()
# Test No matching devices
argv = ["", self.tmpfile, "search",
"--ip", "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_list_parts_ipv4_old_format(self):
self.create_sample_ring()
# Test ipv4(old format)
argv = ["", self.tmpfile, "list_parts",
"d0r0z0-127.0.0.1:6000R127.0.0.1:6000/sda1_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_list_parts_ipv6_old_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(old format)
argv = ["", self.tmpfile, "list_parts",
"d2r2z3-[2001:0000:1234:0000:0000:C1C0:ABCD:0876]:6000"
"R[2::10]:7000/sda3_some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_list_parts_ipv4_new_format(self):
self.create_sample_ring()
# Test ipv4(new format)
argv = \
["", self.tmpfile, "list_parts",
"--id", "0", "--region", "0", "--zone", "0",
"--ip", "127.0.0.1",
"--port", "6000",
"--replication-ip", "127.0.0.1",
"--replication-port", "6000",
"--device", "sda1", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_list_parts_ipv6_new_format(self):
self.create_sample_ring()
# add IPV6
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test ipv6(new format)
argv = \
["", self.tmpfile, "list_parts",
"--id", "2", "--region", "2", "--zone", "3",
"--ip", "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]",
"--port", "6000",
"--replication-ip", "[2::10]",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_list_parts_domain_new_format(self):
self.create_sample_ring()
# add domain name
argv = \
["", self.tmpfile, "add",
"--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data",
"--weight", "3.14159265359"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
# Test domain name
argv = \
["", self.tmpfile, "list_parts",
"--id", "2", "--region", "2", "--zone", "3",
"--ip", "test.test.com",
"--port", "6000",
"--replication-ip", "r.test.com",
"--replication-port", "7000",
"--device", "sda3", "--meta", "some meta data"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_list_parts_number_of_arguments(self):
# Test Number of arguments abnormal
argv = ["", self.tmpfile, "list_parts"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_list_parts_no_matching(self):
self.create_sample_ring()
# Test No matching devices
argv = ["", self.tmpfile, "list_parts",
"--ip", "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_unknown(self):
argv = ["", self.tmpfile, "unknown"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_default(self):
self.create_sample_ring()
argv = ["", self.tmpfile]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_rebalance(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "rebalance", "3"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertTrue(ring.validate())
def test_rebalance_no_device_change(self):
self.create_sample_ring()
ring = RingBuilder.load(self.tmpfile)
ring.rebalance()
ring.save(self.tmpfile)
# Test No change to the device
argv = ["", self.tmpfile, "rebalance", "3"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 1)
def test_rebalance_no_devices(self):
# Test no devices
argv = ["", self.tmpfile, "create", "6", "3.14159265359", "1"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
argv = ["", self.tmpfile, "rebalance"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_write_ring(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "rebalance"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
argv = ["", self.tmpfile, "write_ring"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
def test_write_builder(self):
# Test builder file already exists
self.create_sample_ring()
argv = ["", self.tmpfile, "rebalance"]
self.assertRaises(SystemExit, ringbuilder.main, argv)
argv = ["", self.tmpfile, "write_builder"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 2)
def test_warn_at_risk(self):
# when the number of total part replicas (3 * 2 ** 4 = 48 in
# this ring) is less than the total units of weight (310 in this
# ring) the relative number of parts per unit of weight (called
# weight_of_one_part) is less than 1 - and each whole part
# placed takes up a larger ratio of the fractional number of
# parts the device wants - so it's much more difficult to
# satisfy a device's weight exactly - that is to say less parts
# to go around tends to make things lumpy
self.create_sample_ring(4)
ring = RingBuilder.load(self.tmpfile)
ring.devs[0]['weight'] = 10
ring.save(self.tmpfile)
argv = ["", self.tmpfile, "rebalance"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 1)
def test_no_warn_when_balanced(self):
# when the number of total part replicas (3 * 2 ** 10 = 3072 in
# this ring) is larger than the total units of weight (310 in
# this ring) the relative number of parts per unit of weight
# (called weight_of_one_part) is more than 1 - and each whole
# part placed takes up a smaller ratio of the larger number of
# parts the device wants - so it's much easier to satisfy a
# device's weight exactly - that is to say more parts to go
# around tends to smooth things out
self.create_sample_ring(10)
ring = RingBuilder.load(self.tmpfile)
ring.devs[0]['weight'] = 10
ring.save(self.tmpfile)
argv = ["", self.tmpfile, "rebalance"]
err = None
try:
ringbuilder.main(argv)
except SystemExit as e:
err = e
self.assertEqual(err.code, 0)
def test_invalid_device_name(self):
self.create_sample_ring()
for device_name in ["", " ", " sda1", "sda1 ", " meta "]:
err = 0
argv = ["",
self.tmpfile,
"add",
"r1z1-127.0.0.1:6000/%s" % device_name,
"1"]
try:
ringbuilder.main(argv)
except SystemExit as exc:
err = exc
self.assertEqual(err.code, 2)
argv = ["",
self.tmpfile,
"add",
"--region", "1",
"--zone", "1",
"--ip", "127.0.0.1",
"--port", "6000",
"--device", device_name,
"--weight", "100"]
try:
ringbuilder.main(argv)
except SystemExit as exc:
err = exc
self.assertEqual(err.code, 2)
def test_dispersion_command(self):
self.create_sample_ring()
self.run_srb('rebalance')
out, err = self.run_srb('dispersion -v')
self.assertIn('dispersion', out.lower())
self.assertFalse(err)
def test_use_ringfile_as_builderfile(self):
mock_stdout = six.StringIO()
mock_stderr = six.StringIO()
argv = ["", "object.ring.gz"]
try:
with mock.patch("sys.stdout", mock_stdout):
with mock.patch("sys.stderr", mock_stderr):
ringbuilder.main(argv)
except SystemExit:
pass
expected = "Note: using object.builder instead of object.ring.gz " \
"as builder file\n" \
"Ring Builder file does not exist: object.builder\n"
self.assertEqual(expected, mock_stdout.getvalue())
class TestRebalanceCommand(unittest.TestCase, RunSwiftRingBuilderMixin):
def __init__(self, *args, **kwargs):
super(TestRebalanceCommand, self).__init__(*args, **kwargs)
tmpf = tempfile.NamedTemporaryFile()
self.tempfile = self.tmpfile = tmpf.name
def tearDown(self):
try:
os.remove(self.tempfile)
except OSError:
pass
def run_srb(self, *argv):
mock_stdout = six.StringIO()
mock_stderr = six.StringIO()
srb_args = ["", self.tempfile] + [str(s) for s in argv]
try:
with mock.patch("sys.stdout", mock_stdout):
with mock.patch("sys.stderr", mock_stderr):
ringbuilder.main(srb_args)
except SystemExit as err:
if err.code not in (0, 1): # (success, warning)
raise
return (mock_stdout.getvalue(), mock_stderr.getvalue())
def test_debug(self):
# NB: getLogger(name) always returns the same object
rb_logger = logging.getLogger("swift.ring.builder")
try:
self.assertNotEqual(rb_logger.getEffectiveLevel(), logging.DEBUG)
self.run_srb("create", 8, 3, 1)
self.run_srb("add",
"r1z1-10.1.1.1:2345/sda", 100.0,
"r1z1-10.1.1.1:2345/sdb", 100.0,
"r1z1-10.1.1.1:2345/sdc", 100.0,
"r1z1-10.1.1.1:2345/sdd", 100.0)
self.run_srb("rebalance", "--debug")
self.assertEqual(rb_logger.getEffectiveLevel(), logging.DEBUG)
rb_logger.setLevel(logging.INFO)
self.run_srb("rebalance", "--debug", "123")
self.assertEqual(rb_logger.getEffectiveLevel(), logging.DEBUG)
rb_logger.setLevel(logging.INFO)
self.run_srb("rebalance", "123", "--debug")
self.assertEqual(rb_logger.getEffectiveLevel(), logging.DEBUG)
finally:
rb_logger.setLevel(logging.INFO) # silence other test cases
def test_rebalance_warning_appears(self):
self.run_srb("create", 8, 3, 24)
# all in one machine: totally balanceable
self.run_srb("add",
"r1z1-10.1.1.1:2345/sda", 100.0,
"r1z1-10.1.1.1:2345/sdb", 100.0,
"r1z1-10.1.1.1:2345/sdc", 100.0,
"r1z1-10.1.1.1:2345/sdd", 100.0)
out, err = self.run_srb("rebalance")
self.assertTrue("rebalance/repush" not in out)
# 2 machines of equal size: balanceable, but not in one pass due to
# min_part_hours > 0
self.run_srb("add",
"r1z1-10.1.1.2:2345/sda", 100.0,
"r1z1-10.1.1.2:2345/sdb", 100.0,
"r1z1-10.1.1.2:2345/sdc", 100.0,
"r1z1-10.1.1.2:2345/sdd", 100.0)
self.run_srb("pretend_min_part_hours_passed")
out, err = self.run_srb("rebalance")
self.assertTrue("rebalance/repush" in out)
# after two passes, it's all balanced out
self.run_srb("pretend_min_part_hours_passed")
out, err = self.run_srb("rebalance")
self.assertTrue("rebalance/repush" not in out)
def test_rebalance_warning_with_overload(self):
self.run_srb("create", 8, 3, 24)
self.run_srb("set_overload", 0.12)
# The ring's balance is at least 5, so normally we'd get a warning,
# but it's suppressed due to the overload factor.
self.run_srb("add",
"r1z1-10.1.1.1:2345/sda", 100.0,
"r1z1-10.1.1.1:2345/sdb", 100.0,
"r1z1-10.1.1.1:2345/sdc", 120.0)
out, err = self.run_srb("rebalance")
self.assertTrue("rebalance/repush" not in out)
# Now we add in a really big device, but not enough partitions move
# to fill it in one pass, so we see the rebalance warning.
self.run_srb("add", "r1z1-10.1.1.1:2345/sdd", 99999.0)
self.run_srb("pretend_min_part_hours_passed")
out, err = self.run_srb("rebalance")
self.assertTrue("rebalance/repush" in out)
def test_cached_dispersion_value(self):
self.run_srb("create", 8, 3, 24)
self.run_srb("add",
"r1z1-10.1.1.1:2345/sda", 100.0,
"r1z1-10.1.1.1:2345/sdb", 100.0,
"r1z1-10.1.1.1:2345/sdc", 100.0,
"r1z1-10.1.1.1:2345/sdd", 100.0)
self.run_srb('rebalance')
out, err = self.run_srb() # list devices
self.assertTrue('dispersion' in out)
# remove cached dispersion value
builder = RingBuilder.load(self.tempfile)
builder.dispersion = None
builder.save(self.tempfile)
# now dispersion output is suppressed
out, err = self.run_srb() # list devices
self.assertFalse('dispersion' in out)
# but will show up after rebalance
self.run_srb('rebalance', '-f')
out, err = self.run_srb() # list devices
self.assertTrue('dispersion' in out)
if __name__ == '__main__':
unittest.main()