Add tests for swift-ring-builder

Add some tests for essential methods in swift-ring-builder.
Tests for removing or changing device settings are executed
with different search values to cover many possible command
line arguments.

Currently tested methods:

- create ring
- add device
- remove device
- set weight
- set info
- set min_part_hours
- set replicas

Tests use swift.common.ring.RingBuilder to verify actions.

Catching and testing output from print statements is not
tested, because this requires redirecting sys.stdout during
tests and that might have some sideeffects for testing tools.

bin/swift-ring-builder has been moved to swift/cli/ringbuilder.py
and slightly modified to work as before (mainly due to no more
existing global variables since that part of the code has been
moved inside a main() function).

Change-Id: Ia63f59a8faca1fad990784f27532ca07a2125454
This commit is contained in:
Christian Schwede 2014-02-05 06:51:46 +00:00
parent b1cacf4c93
commit 24657b2b39
3 changed files with 221 additions and 3 deletions

View File

@ -53,7 +53,6 @@ scripts =
bin/swift-orphans
bin/swift-proxy-server
bin/swift-recon-cron
bin/swift-ring-builder
bin/swift-temp-url
[entry_points]
@ -90,6 +89,7 @@ paste.filter_factory =
console_scripts =
swift-recon = swift.cli.recon:main
swift-ring-builder = swift.cli.ringbuilder:main
[build_sphinx]
all_files = 1

View File

@ -20,7 +20,7 @@ from itertools import islice, izip
from math import ceil
from os import mkdir
from os.path import basename, abspath, dirname, exists, join as pathjoin
from sys import argv, exit, stderr
from sys import argv as sys_argv, exit, stderr
from textwrap import wrap
from time import time
@ -37,6 +37,9 @@ EXIT_SUCCESS = 0
EXIT_WARNING = 1
EXIT_ERROR = 2
global argv, backup_dir, builder, builder_file, ring_file
argv = backup_dir = builder = builder_file = ring_file = None
def format_device(dev):
"""
@ -796,7 +799,14 @@ swift-ring-builder <builder_file> set_replicas <replicas>
builder.save(argv[1])
exit(EXIT_SUCCESS)
if __name__ == '__main__':
def main(arguments=None):
global argv, backup_dir, builder, builder_file, ring_file
if arguments:
argv = arguments
else:
argv = sys_argv
if len(argv) < 2:
print "swift-ring-builder %(MAJOR_VERSION)s.%(MINOR_VERSION)s\n" % \
globals()
@ -846,3 +856,7 @@ if __name__ == '__main__':
exit(2)
else:
Commands.__dict__.get(command, Commands.unknown.im_func)()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,204 @@
# 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 os
import tempfile
import unittest
import swift.cli.ringbuilder
from swift.common.ring import RingBuilder
class TestCommands(unittest.TestCase):
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.tmpfile = tmpf.name
def tearDown(self):
try:
os.remove(self.tmpfile)
except OSError:
pass
def create_sample_ring(self):
""" Create a sample ring with two devices
At least two devices are needed to test removing
a device, since removing the last device of a ring
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(6, 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.save(self.tmpfile)
def test_create_ring(self):
argv = ["", self.tmpfile, "create", "6", "3.14159265359", "1"]
self.assertRaises(SystemExit, swift.cli.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(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "add",
"r2z3-127.0.0.1:6000/sda3_some meta data", "3.14159265359"]
self.assertRaises(SystemExit, swift.cli.ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = [d for d in ring.devs if d['id'] == 2][0]
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')
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
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, swift.cli.ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was set to 0
dev = [d for d in ring.devs if d['id'] == 0][0]
self.assertEqual(dev['weight'], 0)
# Check that device is in list of devices to be removed
dev = [d for d in ring._remove_devs if d['id'] == 0][0]
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 = [d for d in ring.devs if d['id'] == 1][0]
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_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, swift.cli.ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
# Check that weight was changed
dev = [d for d in ring.devs if d['id'] == 0][0]
self.assertEqual(dev['weight'], 3.14159265359)
# Check that second device in ring is not affected
dev = [d for d in ring.devs if d['id'] == 1][0]
self.assertEqual(dev['weight'], 100)
# Final check, rebalance and check ring is ok
ring.rebalance()
self.assertTrue(ring.validate())
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, swift.cli.ringbuilder.main, argv)
# Check that device was created with given data
ring = RingBuilder.load(self.tmpfile)
dev = [d for d in ring.devs if d['id'] == 0][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 = [d for d in ring.devs if d['id'] == 1][0]
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_min_part_hours(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_min_part_hours", "24"]
self.assertRaises(SystemExit, swift.cli.ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.min_part_hours, 24)
def test_set_replicas(self):
self.create_sample_ring()
argv = ["", self.tmpfile, "set_replicas", "3.14159265359"]
self.assertRaises(SystemExit, swift.cli.ringbuilder.main, argv)
ring = RingBuilder.load(self.tmpfile)
self.assertEqual(ring.replicas, 3.14159265359)
if __name__ == '__main__':
unittest.main()