Enable python34 tests for nova/tests/unit/objects/test*.py
All tests in nova/tests/unit/objects/test*.py now run with python3.4 tox target. * Fix imports in limits.py and urlmap.py * Add a list() as keys()/values() return iterator in availability_zones.py, flavors.py and instance_numa_topology.py * contextlib.nested is not present in python3, so whip up an alternative using ExitStack(). Directly using ExitStack() wont work for us. * Add a few assertJsonEqual in the test cases * Ensure fingerprinting generates the same exact value in both python27 and python34 Blueprint nova-python3 Change-Id: I848c48475189c4b4ad8151e14509020ae7d110a4
This commit is contained in:
parent
e4e16e9077
commit
9f698dc296
@ -33,13 +33,13 @@ process (each process will have its own rate limiting counter).
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import copy
|
import copy
|
||||||
import httplib
|
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
from six.moves import http_client as httplib
|
||||||
import webob.dec
|
import webob.dec
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
@ -14,10 +14,15 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import urllib2
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import paste.urlmap
|
import paste.urlmap
|
||||||
|
import six
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
from urllib import request as urllib2
|
||||||
|
else:
|
||||||
|
import urllib2
|
||||||
|
|
||||||
from nova.api.openstack import wsgi
|
from nova.api.openstack import wsgi
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ def _build_metadata_by_host(aggregates, hosts=None):
|
|||||||
for host in aggregate.hosts:
|
for host in aggregate.hosts:
|
||||||
if hosts and host not in hosts:
|
if hosts and host not in hosts:
|
||||||
continue
|
continue
|
||||||
metadata[host].add(aggregate.metadata.values()[0])
|
metadata[host].add(list(aggregate.metadata.values())[0])
|
||||||
return metadata
|
return metadata
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ def delete_flavor_info(metadata, *prefixes):
|
|||||||
# NUMA-related ones that we need to avoid an uglier alternative. This
|
# NUMA-related ones that we need to avoid an uglier alternative. This
|
||||||
# should be replaced by a general split-out of flavor information from
|
# should be replaced by a general split-out of flavor information from
|
||||||
# system_metadata very soon.
|
# system_metadata very soon.
|
||||||
for key in metadata.keys():
|
for key in list(metadata.keys()):
|
||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
if key.startswith('%sinstance_type_extra_' % prefix):
|
if key.startswith('%sinstance_type_extra_' % prefix):
|
||||||
del metadata[key]
|
del metadata[key]
|
||||||
|
@ -91,7 +91,7 @@ class InstanceNUMACell(base.NovaObject,
|
|||||||
if threads == 1:
|
if threads == 1:
|
||||||
threads = 0
|
threads = 0
|
||||||
|
|
||||||
return map(set, zip(*[iter(cpu_list)] * threads))
|
return list(map(set, zip(*[iter(cpu_list)] * threads)))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cpu_pinning_requested(self):
|
def cpu_pinning_requested(self):
|
||||||
|
33
nova/test.py
33
nova/test.py
@ -20,6 +20,8 @@ Allows overriding of flags for use of fakes, and some black magic for
|
|||||||
inline callbacks.
|
inline callbacks.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import contextlib
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import eventlet
|
import eventlet
|
||||||
eventlet.monkey_patch(os=False)
|
eventlet.monkey_patch(os=False)
|
||||||
@ -67,6 +69,14 @@ objects.register_all()
|
|||||||
|
|
||||||
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def nested(*contexts):
|
||||||
|
with contextlib.ExitStack() as stack:
|
||||||
|
yield [stack.enter_context(c) for c in contexts]
|
||||||
|
else:
|
||||||
|
nested = contextlib.nested
|
||||||
|
|
||||||
|
|
||||||
class SampleNetworks(fixtures.Fixture):
|
class SampleNetworks(fixtures.Fixture):
|
||||||
|
|
||||||
@ -285,24 +295,25 @@ class TestCase(testtools.TestCase):
|
|||||||
observed = jsonutils.loads(observed)
|
observed = jsonutils.loads(observed)
|
||||||
|
|
||||||
def sort(what):
|
def sort(what):
|
||||||
return sorted(what,
|
def get_key(item):
|
||||||
key=lambda x: str(x) if isinstance(
|
if isinstance(item, (datetime.datetime, set)):
|
||||||
x, set) or isinstance(x,
|
return str(item)
|
||||||
datetime.datetime) else x)
|
if six.PY3 and isinstance(item, dict):
|
||||||
|
return str(sort(list(six.iterkeys(item)) +
|
||||||
|
list(six.itervalues(item))))
|
||||||
|
return str(item) if six.PY3 else item
|
||||||
|
|
||||||
|
return sorted(what, key=get_key)
|
||||||
|
|
||||||
def inner(expected, observed):
|
def inner(expected, observed):
|
||||||
if isinstance(expected, dict) and isinstance(observed, dict):
|
if isinstance(expected, dict) and isinstance(observed, dict):
|
||||||
self.assertEqual(len(expected), len(observed))
|
self.assertEqual(len(expected), len(observed))
|
||||||
expected_keys = sorted(expected)
|
expected_keys = sorted(expected)
|
||||||
observed_keys = sorted(expected)
|
observed_keys = sorted(observed)
|
||||||
self.assertEqual(expected_keys, observed_keys)
|
self.assertEqual(expected_keys, observed_keys)
|
||||||
|
|
||||||
expected_values_iter = iter(sort(expected.values()))
|
for key in list(six.iterkeys(expected)):
|
||||||
observed_values_iter = iter(sort(observed.values()))
|
inner(expected[key], observed[key])
|
||||||
|
|
||||||
for i in range(len(expected)):
|
|
||||||
inner(next(expected_values_iter),
|
|
||||||
next(observed_values_iter))
|
|
||||||
elif (isinstance(expected, (list, tuple, set)) and
|
elif (isinstance(expected, (list, tuple, set)) and
|
||||||
isinstance(observed, (list, tuple, set))):
|
isinstance(observed, (list, tuple, set))):
|
||||||
self.assertEqual(len(expected), len(observed))
|
self.assertEqual(len(expected), len(observed))
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import contextlib
|
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import six
|
import six
|
||||||
|
|
||||||
@ -53,7 +51,7 @@ class _TestBlockDeviceMappingObject(object):
|
|||||||
self.flags(enable=False, group='cells')
|
self.flags(enable=False, group='cells')
|
||||||
|
|
||||||
fake_bdm = self.fake_bdm()
|
fake_bdm = self.fake_bdm()
|
||||||
with contextlib.nested(
|
with test.nested(
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
db, 'block_device_mapping_update', return_value=fake_bdm),
|
db, 'block_device_mapping_update', return_value=fake_bdm),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
@ -149,7 +147,7 @@ class _TestBlockDeviceMappingObject(object):
|
|||||||
values['device_name'] = device_name
|
values['device_name'] = device_name
|
||||||
fake_bdm = fake_block_device.FakeDbBlockDeviceDict(values)
|
fake_bdm = fake_block_device.FakeDbBlockDeviceDict(values)
|
||||||
|
|
||||||
with contextlib.nested(
|
with test.nested(
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
db, 'block_device_mapping_create', return_value=fake_bdm),
|
db, 'block_device_mapping_create', return_value=fake_bdm),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
@ -247,7 +245,7 @@ class _TestBlockDeviceMappingObject(object):
|
|||||||
self.flags(enable=True, cell_type=cell_type, group='cells')
|
self.flags(enable=True, cell_type=cell_type, group='cells')
|
||||||
else:
|
else:
|
||||||
self.flags(enable=False, group='cells')
|
self.flags(enable=False, group='cells')
|
||||||
with contextlib.nested(
|
with test.nested(
|
||||||
mock.patch.object(db, 'block_device_mapping_destroy'),
|
mock.patch.object(db, 'block_device_mapping_destroy'),
|
||||||
mock.patch.object(cells_rpcapi.CellsAPI, 'bdm_destroy_at_top')
|
mock.patch.object(cells_rpcapi.CellsAPI, 'bdm_destroy_at_top')
|
||||||
) as (bdm_del, cells_destroy):
|
) as (bdm_del, cells_destroy):
|
||||||
|
@ -131,7 +131,7 @@ class _TestInstanceObject(object):
|
|||||||
exp_cols.remove('pci_requests')
|
exp_cols.remove('pci_requests')
|
||||||
exp_cols.remove('vcpu_model')
|
exp_cols.remove('vcpu_model')
|
||||||
exp_cols.remove('ec2_ids')
|
exp_cols.remove('ec2_ids')
|
||||||
exp_cols = filter(lambda x: 'flavor' not in x, exp_cols)
|
exp_cols = list(filter(lambda x: 'flavor' not in x, exp_cols))
|
||||||
exp_cols.extend(['extra', 'extra.numa_topology', 'extra.pci_requests',
|
exp_cols.extend(['extra', 'extra.numa_topology', 'extra.pci_requests',
|
||||||
'extra.flavor', 'extra.vcpu_model'])
|
'extra.flavor', 'extra.vcpu_model'])
|
||||||
|
|
||||||
@ -473,7 +473,7 @@ class _TestInstanceObject(object):
|
|||||||
actual_args = mock_update.call_args
|
actual_args = mock_update.call_args
|
||||||
self.assertEqual(self.context, actual_args[0][0])
|
self.assertEqual(self.context, actual_args[0][0])
|
||||||
self.assertEqual(inst.uuid, actual_args[0][1])
|
self.assertEqual(inst.uuid, actual_args[0][1])
|
||||||
self.assertEqual(actual_args[0][2].keys(), ['vcpu_model'])
|
self.assertEqual(list(actual_args[0][2].keys()), ['vcpu_model'])
|
||||||
self.assertJsonEqual(jsonutils.dumps(
|
self.assertJsonEqual(jsonutils.dumps(
|
||||||
test_vcpu_model.fake_vcpumodel.obj_to_primitive()),
|
test_vcpu_model.fake_vcpumodel.obj_to_primitive()),
|
||||||
actual_args[0][2]['vcpu_model'])
|
actual_args[0][2]['vcpu_model'])
|
||||||
@ -1003,9 +1003,9 @@ class _TestInstanceObject(object):
|
|||||||
expected = {}
|
expected = {}
|
||||||
for key in unicode_attributes:
|
for key in unicode_attributes:
|
||||||
inst[key] = u'\u2603'
|
inst[key] = u'\u2603'
|
||||||
expected[key] = '?'
|
expected[key] = b'?'
|
||||||
primitive = inst.obj_to_primitive(target_version='1.6')
|
primitive = inst.obj_to_primitive(target_version='1.6')
|
||||||
self.assertEqual(expected, primitive['nova_object.data'])
|
self.assertJsonEqual(expected, primitive['nova_object.data'])
|
||||||
self.assertEqual('1.6', primitive['nova_object.version'])
|
self.assertEqual('1.6', primitive['nova_object.version'])
|
||||||
|
|
||||||
def test_compat_pci_devices(self):
|
def test_compat_pci_devices(self):
|
||||||
@ -1652,7 +1652,7 @@ class _TestInstanceListObject(object):
|
|||||||
inst_list._context = self.context
|
inst_list._context = self.context
|
||||||
inst_list.objects = insts
|
inst_list.objects = insts
|
||||||
faulty = inst_list.fill_faults()
|
faulty = inst_list.fill_faults()
|
||||||
self.assertEqual(faulty, ['uuid1'])
|
self.assertEqual(list(faulty), ['uuid1'])
|
||||||
self.assertEqual(inst_list[0].fault.message,
|
self.assertEqual(inst_list[0].fault.message,
|
||||||
db_faults['uuid1'][0]['message'])
|
db_faults['uuid1'][0]['message'])
|
||||||
self.assertIsNone(inst_list[1].fault)
|
self.assertIsNone(inst_list[1].fault)
|
||||||
|
@ -498,8 +498,8 @@ class _TestObject(object):
|
|||||||
error = None
|
error = None
|
||||||
try:
|
try:
|
||||||
base.NovaObject.obj_class_from_name('MyObj', '1.25')
|
base.NovaObject.obj_class_from_name('MyObj', '1.25')
|
||||||
except exception.IncompatibleObjectVersion as error:
|
except exception.IncompatibleObjectVersion as ex:
|
||||||
pass
|
error = ex
|
||||||
|
|
||||||
self.assertIsNotNone(error)
|
self.assertIsNotNone(error)
|
||||||
self.assertEqual('1.6', error.kwargs['supported'])
|
self.assertEqual('1.6', error.kwargs['supported'])
|
||||||
@ -646,7 +646,7 @@ class _TestObject(object):
|
|||||||
myobj_fields = (['foo', 'bar', 'missing',
|
myobj_fields = (['foo', 'bar', 'missing',
|
||||||
'readonly', 'rel_object',
|
'readonly', 'rel_object',
|
||||||
'rel_objects', 'mutable_default'] +
|
'rel_objects', 'mutable_default'] +
|
||||||
base_fields)
|
list(base_fields))
|
||||||
myobj3_fields = ['new_field']
|
myobj3_fields = ['new_field']
|
||||||
self.assertTrue(issubclass(TestSubclassedObject, MyObj))
|
self.assertTrue(issubclass(TestSubclassedObject, MyObj))
|
||||||
self.assertEqual(len(myobj_fields), len(MyObj.fields))
|
self.assertEqual(len(myobj_fields), len(MyObj.fields))
|
||||||
@ -1192,11 +1192,19 @@ object_relationships = {
|
|||||||
|
|
||||||
|
|
||||||
class TestObjectVersions(test.NoDBTestCase):
|
class TestObjectVersions(test.NoDBTestCase):
|
||||||
|
@staticmethod
|
||||||
|
def _is_method(thing):
|
||||||
|
# NOTE(dims): In Python3, The concept of 'unbound methods' has
|
||||||
|
# been removed from the language. When referencing a method
|
||||||
|
# as a class attribute, you now get a plain function object.
|
||||||
|
# so let's check for both
|
||||||
|
return inspect.isfunction(thing) or inspect.ismethod(thing)
|
||||||
|
|
||||||
def _find_remotable_method(self, cls, thing, parent_was_remotable=False):
|
def _find_remotable_method(self, cls, thing, parent_was_remotable=False):
|
||||||
"""Follow a chain of remotable things down to the original function."""
|
"""Follow a chain of remotable things down to the original function."""
|
||||||
if isinstance(thing, classmethod):
|
if isinstance(thing, classmethod):
|
||||||
return self._find_remotable_method(cls, thing.__get__(None, cls))
|
return self._find_remotable_method(cls, thing.__get__(None, cls))
|
||||||
elif inspect.ismethod(thing) and hasattr(thing, 'remotable'):
|
elif self._is_method(thing) and hasattr(thing, 'remotable'):
|
||||||
return self._find_remotable_method(cls, thing.original_fn,
|
return self._find_remotable_method(cls, thing.original_fn,
|
||||||
parent_was_remotable=True)
|
parent_was_remotable=True)
|
||||||
elif parent_was_remotable:
|
elif parent_was_remotable:
|
||||||
@ -1210,12 +1218,12 @@ class TestObjectVersions(test.NoDBTestCase):
|
|||||||
def _get_fingerprint(self, obj_name):
|
def _get_fingerprint(self, obj_name):
|
||||||
obj_classes = base.NovaObjectRegistry.obj_classes()
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
obj_class = obj_classes[obj_name][0]
|
obj_class = obj_classes[obj_name][0]
|
||||||
fields = obj_class.fields.items()
|
fields = list(obj_class.fields.items())
|
||||||
fields.sort()
|
fields.sort()
|
||||||
methods = []
|
methods = []
|
||||||
for name in dir(obj_class):
|
for name in dir(obj_class):
|
||||||
thing = getattr(obj_class, name)
|
thing = getattr(obj_class, name)
|
||||||
if inspect.ismethod(thing) or isinstance(thing, classmethod):
|
if self._is_method(thing) or isinstance(thing, classmethod):
|
||||||
method = self._find_remotable_method(obj_class, thing)
|
method = self._find_remotable_method(obj_class, thing)
|
||||||
if method:
|
if method:
|
||||||
methods.append((name, inspect.getargspec(method)))
|
methods.append((name, inspect.getargspec(method)))
|
||||||
@ -1231,14 +1239,26 @@ class TestObjectVersions(test.NoDBTestCase):
|
|||||||
sorted(obj_class.child_versions.items())))
|
sorted(obj_class.child_versions.items())))
|
||||||
else:
|
else:
|
||||||
relevant_data = (fields, methods)
|
relevant_data = (fields, methods)
|
||||||
fingerprint = '%s-%s' % (obj_class.VERSION,
|
relevant_data = repr(relevant_data)
|
||||||
hashlib.md5(str(relevant_data)).hexdigest())
|
if six.PY3:
|
||||||
|
relevant_data = relevant_data.encode('utf-8')
|
||||||
|
fingerprint = '%s-%s' % (
|
||||||
|
obj_class.VERSION, hashlib.md5(relevant_data).hexdigest())
|
||||||
return fingerprint
|
return fingerprint
|
||||||
|
|
||||||
|
def test_find_remotable_method(self):
|
||||||
|
class MyObject(object):
|
||||||
|
@base.remotable
|
||||||
|
def my_method(self):
|
||||||
|
return 'Hello World!'
|
||||||
|
thing = self._find_remotable_method(MyObject,
|
||||||
|
getattr(MyObject, 'my_method'))
|
||||||
|
self.assertIsNotNone(thing)
|
||||||
|
|
||||||
def test_versions(self):
|
def test_versions(self):
|
||||||
fingerprints = {}
|
fingerprints = {}
|
||||||
obj_classes = base.NovaObjectRegistry.obj_classes()
|
obj_classes = base.NovaObjectRegistry.obj_classes()
|
||||||
for obj_name in obj_classes:
|
for obj_name in sorted(obj_classes, key=lambda x: x[0]):
|
||||||
fingerprints[obj_name] = self._get_fingerprint(obj_name)
|
fingerprints[obj_name] = self._get_fingerprint(obj_name)
|
||||||
|
|
||||||
if os.getenv('GENERATE_HASHES'):
|
if os.getenv('GENERATE_HASHES'):
|
||||||
|
40
tox.ini
40
tox.ini
@ -44,7 +44,45 @@ commands =
|
|||||||
find . -type f -name "*.pyc" -delete
|
find . -type f -name "*.pyc" -delete
|
||||||
python -m testtools.run \
|
python -m testtools.run \
|
||||||
nova.tests.unit.db.test_db_api \
|
nova.tests.unit.db.test_db_api \
|
||||||
nova.tests.unit.test_versions
|
nova.tests.unit.test_versions \
|
||||||
|
nova.tests.unit.objects.test_agent \
|
||||||
|
nova.tests.unit.objects.test_aggregate \
|
||||||
|
nova.tests.unit.objects.test_bandwidth_usage \
|
||||||
|
nova.tests.unit.objects.test_block_device \
|
||||||
|
nova.tests.unit.objects.test_cell_mapping \
|
||||||
|
nova.tests.unit.objects.test_compute_node \
|
||||||
|
nova.tests.unit.objects.test_dns_domain \
|
||||||
|
nova.tests.unit.objects.test_ec2 \
|
||||||
|
nova.tests.unit.objects.test_external_event \
|
||||||
|
nova.tests.unit.objects.test_fields \
|
||||||
|
nova.tests.unit.objects.test_fixed_ip \
|
||||||
|
nova.tests.unit.objects.test_flavor \
|
||||||
|
nova.tests.unit.objects.test_floating_ip \
|
||||||
|
nova.tests.unit.objects.test_hv_spec \
|
||||||
|
nova.tests.unit.objects.test_instance \
|
||||||
|
nova.tests.unit.objects.test_instance_action \
|
||||||
|
nova.tests.unit.objects.test_instance_fault \
|
||||||
|
nova.tests.unit.objects.test_instance_group \
|
||||||
|
nova.tests.unit.objects.test_instance_info_cache \
|
||||||
|
nova.tests.unit.objects.test_instance_mapping \
|
||||||
|
nova.tests.unit.objects.test_instance_numa_topology \
|
||||||
|
nova.tests.unit.objects.test_instance_pci_requests \
|
||||||
|
nova.tests.unit.objects.test_keypair \
|
||||||
|
nova.tests.unit.objects.test_migration \
|
||||||
|
nova.tests.unit.objects.test_network \
|
||||||
|
nova.tests.unit.objects.test_network_request \
|
||||||
|
nova.tests.unit.objects.test_numa \
|
||||||
|
nova.tests.unit.objects.test_objects \
|
||||||
|
nova.tests.unit.objects.test_pci_device \
|
||||||
|
nova.tests.unit.objects.test_pci_device_pool \
|
||||||
|
nova.tests.unit.objects.test_quotas \
|
||||||
|
nova.tests.unit.objects.test_security_group \
|
||||||
|
nova.tests.unit.objects.test_security_group_rule \
|
||||||
|
nova.tests.unit.objects.test_service \
|
||||||
|
nova.tests.unit.objects.test_tag \
|
||||||
|
nova.tests.unit.objects.test_vcpu_model \
|
||||||
|
nova.tests.unit.objects.test_virt_cpu_topology \
|
||||||
|
nova.tests.unit.objects.test_virtual_interface
|
||||||
|
|
||||||
[testenv:functional]
|
[testenv:functional]
|
||||||
usedevelop = True
|
usedevelop = True
|
||||||
|
Loading…
Reference in New Issue
Block a user