object-storage: use tox for unit tests; fix em too

Add the ability to use tox for unit tests, since it helps us solve the
problem of supporting multiple branches that require different
versions of dependencies, and allows us to possibly support multiple
versions of python in the future.

Also fix the code to work with pre-grizzly environments, by not
requiring the constraints backport.

Also fixed the xattr support to work with both pyxattr and xattr
modules.

And fixed the ring tests to also work without a live /etc/swift
directory.

BUG: 948657 (https://bugzilla.redhat.com/show_bug.cgi?id=948657)
Change-Id: I2be79c8ef8916bb6552ef957094f9186a963a068
Signed-off-by: Peter Portante <peter.portante@redhat.com>
Reviewed-on: http://review.gluster.org/4781
Reviewed-by: Alex Wheeler <wheelear@gmail.com>
Tested-by: Alex Wheeler <wheelear@gmail.com>
Reviewed-by: Anand Avati <avati@redhat.com>
This commit is contained in:
Peter Portante 2013-04-04 13:08:32 -04:00 committed by Anand Avati
parent a56dca94c3
commit 6a7d28c0f8
19 changed files with 91 additions and 76 deletions

2
.gitignore vendored
View File

@ -22,6 +22,8 @@ Makefile
stamp-h1
# Generated files
ufo/.tox
ufo/test/unit/.coverage
contrib/uuid/uuid_types.h
extras/init.d/glusterd.plist
extras/init.d/glusterd-Debian

View File

@ -35,8 +35,8 @@ do
zone=1
for volname in $@
do
add $builder_file $zone ${port[$builder_file]} $volname
zone=$(expr $zone + 1)
add $builder_file $zone ${port[$builder_file]} $volname
zone=$(expr $zone + 1)
done
rebalance $builder_file

View File

@ -13,15 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from webob.exc import HTTPBadRequest
try:
from webob.exc import HTTPBadRequest
except ImportError:
from swift.common.swob import HTTPBadRequest
import swift.common.constraints
import swift.common.ring as _ring
from gluster.swift.common import Glusterfs, ring
MAX_OBJECT_NAME_COMPONENT_LENGTH = swift.common.constraints.constraints_conf_int(
'max_object_name_component_length', 255)
if hasattr(swift.common.constraints, 'constraints_conf_int'):
MAX_OBJECT_NAME_COMPONENT_LENGTH = \
swift.common.constraints.constraints_conf_int(
'max_object_name_component_length', 255)
else:
MAX_OBJECT_NAME_COMPONENT_LENGTH = 255
def validate_obj_name_component(obj):
if len(obj) > MAX_OBJECT_NAME_COMPONENT_LENGTH:

View File

@ -70,7 +70,7 @@ def read_metadata(path):
key = 0
while metadata is None:
try:
metadata_s += xattr.get(path, '%s%s' % (METADATA_KEY, (key or '')))
metadata_s += xattr.getxattr(path, '%s%s' % (METADATA_KEY, (key or '')))
except IOError as err:
if err.errno == errno.ENODATA:
if key > 0:
@ -86,7 +86,7 @@ def read_metadata(path):
# to the caller we have no metadata.
metadata = {}
else:
logging.exception("xattr.get failed on %s key %s err: %s",
logging.exception("xattr.getxattr failed on %s key %s err: %s",
path, key, str(err))
# Note that we don't touch the keys on errors fetching the
# data since it could be a transient state.
@ -120,9 +120,9 @@ def write_metadata(path, metadata):
key = 0
while metastr:
try:
xattr.set(path, '%s%s' % (METADATA_KEY, key or ''), metastr[:MAX_XATTR_SIZE])
xattr.setxattr(path, '%s%s' % (METADATA_KEY, key or ''), metastr[:MAX_XATTR_SIZE])
except IOError as err:
logging.exception("xattr.set failed on %s key %s err: %s", path, key, str(err))
logging.exception("setxattr failed on %s key %s err: %s", path, key, str(err))
raise
metastr = metastr[MAX_XATTR_SIZE:]
key += 1
@ -131,7 +131,7 @@ def clean_metadata(path):
key = 0
while True:
try:
xattr.remove(path, '%s%s' % (METADATA_KEY, (key or '')))
xattr.removexattr(path, '%s%s' % (METADATA_KEY, (key or '')))
except IOError as err:
if err.errno == errno.ENODATA:
break
@ -142,12 +142,12 @@ def check_user_xattr(path):
if not os_path.exists(path):
return False
try:
xattr.set(path, 'user.test.key1', 'value1')
xattr.setxattr(path, 'user.test.key1', 'value1')
except IOError as err:
logging.exception("check_user_xattr: set failed on %s err: %s", path, str(err))
raise
try:
xattr.remove(path, 'user.test.key1')
xattr.removexattr(path, 'user.test.key1')
except IOError as err:
logging.exception("check_user_xattr: remove failed on %s err: %s", path, str(err))
#Remove xattr may fail in case of concurrent remove.

View File

@ -0,0 +1,3 @@
The unit tests expect certain ring data built using the following command:
../../../../bin/gluster-swift-gen-builders test iops

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -13,69 +13,43 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import unittest
import gluster.swift.common.constraints
from gluster.swift.common.ring import *
from gluster.swift.common.Glusterfs import SWIFT_DIR
import swift.common.utils
from gluster.swift.common.ring import Ring
def _mock_ring_data():
return [{'zone': 1, 'weight': 100.0, 'ip': '127.0.0.1', 'port': 6012, \
'meta': '', 'device': 'test', 'id': 0},
{'zone': 2, 'weight': 100.0, 'ip': '127.0.0.1', 'id': 1, \
'meta': '', 'device': 'iops', 'port': 6012}]
class TestRing(unittest.TestCase):
""" Tests for common.utils """
def setUp(self):
self.ring = Ring(SWIFT_DIR, ring_name='object')
swift.common.utils.HASH_PATH_SUFFIX = 'endcap'
swiftdir = os.path.join(os.getcwd(), "common", "data")
self.ring = Ring(swiftdir, ring_name='object')
def test_first_device(self):
try:
__devs = self.ring._devs
self.ring._devs = _mock_ring_data()
part, node = self.ring.get_nodes('test')
assert node[0]['device'] == 'test'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'test'
for node in self.ring.get_more_nodes(0):
assert node['device'] == 'volume_not_in_ring'
finally:
self.ring._devs = __devs
part, node = self.ring.get_nodes('test')
assert node[0]['device'] == 'test'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'test'
for node in self.ring.get_more_nodes(0):
assert node['device'] == 'volume_not_in_ring'
def test_invalid_device(self):
try:
__devs = self.ring._devs
self.ring._devs = _mock_ring_data()
part, node = self.ring.get_nodes('test2')
assert node[0]['device'] == 'volume_not_in_ring'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'volume_not_in_ring'
finally:
self.ring._devs = __devs
part, node = self.ring.get_nodes('test2')
assert node[0]['device'] == 'volume_not_in_ring'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'volume_not_in_ring'
def test_second_device(self):
try:
__devs = self.ring._devs
self.ring._devs = _mock_ring_data()
part, node = self.ring.get_nodes('iops')
assert node[0]['device'] == 'iops'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'iops'
for node in self.ring.get_more_nodes(0):
assert node['device'] == 'volume_not_in_ring'
finally:
self.ring._devs = __devs
part, node = self.ring.get_nodes('iops')
assert node[0]['device'] == 'iops'
node = self.ring.get_part_nodes(0)
assert node[0]['device'] == 'iops'
for node in self.ring.get_more_nodes(0):
assert node['device'] == 'volume_not_in_ring'
def test_second_device_with_reseller_prefix(self):
try:
__devs = self.ring._devs
self.ring._devs = _mock_ring_data()
part, node = self.ring.get_nodes('AUTH_iops')
assert node[0]['device'] == 'iops'
finally:
self.ring._devs = __devs
part, node = self.ring.get_nodes('AUTH_iops')
assert node[0]['device'] == 'iops'

View File

@ -41,7 +41,7 @@ _xattr_rem_err = {}
def _xkey(path, key):
return "%s:%s" % (path, key)
def _setxattr(path, key, value):
def _setxattr(path, key, value, *args, **kwargs):
_xattr_op_cnt['set'] += 1
xkey = _xkey(path, key)
if xkey in _xattr_set_err:
@ -51,7 +51,7 @@ def _setxattr(path, key, value):
global _xattrs
_xattrs[xkey] = value
def _getxattr(path, key):
def _getxattr(path, key, *args, **kwargs):
_xattr_op_cnt['get'] += 1
xkey = _xkey(path, key)
if xkey in _xattr_get_err:
@ -67,7 +67,7 @@ def _getxattr(path, key):
raise e
return ret_val
def _removexattr(path, key):
def _removexattr(path, key, *args, **kwargs):
_xattr_op_cnt['remove'] += 1
xkey = _xkey(path, key)
if xkey in _xattr_rem_err:
@ -93,20 +93,20 @@ def _initxattr():
_xattr_rem_err = {}
# Save the current methods
global _xattr_set; _xattr_set = xattr.set
global _xattr_get; _xattr_get = xattr.get
global _xattr_remove; _xattr_remove = xattr.remove
global _xattr_set; _xattr_set = xattr.setxattr
global _xattr_get; _xattr_get = xattr.getxattr
global _xattr_remove; _xattr_remove = xattr.removexattr
# Monkey patch the calls we use with our internal unit test versions
xattr.set = _setxattr
xattr.get = _getxattr
xattr.remove = _removexattr
xattr.setxattr = _setxattr
xattr.getxattr = _getxattr
xattr.removexattr = _removexattr
def _destroyxattr():
# Restore the current methods just in case
global _xattr_set; xattr.set = _xattr_set
global _xattr_get; xattr.get = _xattr_get
global _xattr_remove; xattr.remove = _xattr_remove
global _xattr_set; xattr.setxattr = _xattr_set
global _xattr_get; xattr.getxattr = _xattr_get
global _xattr_remove; xattr.removexattr = _xattr_remove
# Destroy the stored values and
global _xattrs; _xattrs = None

6
ufo/tools/test-requires Normal file
View File

@ -0,0 +1,6 @@
coverage
nose
nosexcover
openstack.nose_plugin
nosehtmloutput
mock>=0.8.0

25
ufo/tox.ini Normal file
View File

@ -0,0 +1,25 @@
[tox]
envlist = py26,py27
[testenv]
setenv = VIRTUAL_ENV={envdir}
NOSE_WITH_OPENSTACK=1
NOSE_OPENSTACK_COLOR=1
NOSE_OPENSTACK_RED=0.05
NOSE_OPENSTACK_YELLOW=0.025
NOSE_OPENSTACK_SHOW_ELAPSED=1
NOSE_OPENSTACK_STDOUT=1
deps =
https://launchpad.net/swift/grizzly/1.8.0/+download/swift-1.8.0.tar.gz
-r{toxinidir}/tools/test-requires
changedir = {toxinidir}/test/unit
commands = nosetests -v --exe --with-coverage --cover-package gluster --cover-erase {posargs}
[tox:jenkins]
downloadcache = ~/cache/pip
[testenv:cover]
setenv = NOSE_WITH_COVERAGE=1
[testenv:venv]
commands = {posargs}