diff options
| author | Salvatore Orlando <salvatore.orlando@eu.citrix.com> | 2011-03-24 02:04:57 +0000 |
|---|---|---|
| committer | Salvatore Orlando <salvatore.orlando@eu.citrix.com> | 2011-03-24 02:04:57 +0000 |
| commit | f0a44da43c804d689e8174957f35dbe2f857acdc (patch) | |
| tree | 3448d347bac3f173d9b1d7626541e22c1b4296e5 /nova/tests | |
| parent | 52bd70d500e7e82acea55c8d23c3fd1d66555cc0 (diff) | |
| parent | 20d77ea69bde623cf50531647d9d20ffc7e4bc88 (diff) | |
merge trunk
Diffstat (limited to 'nova/tests')
| -rw-r--r-- | nova/tests/api/openstack/fakes.py | 20 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_images.py | 44 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 22 | ||||
| -rw-r--r-- | nova/tests/image/__init__.py | 16 | ||||
| -rw-r--r-- | nova/tests/image/test_glance.py | 188 | ||||
| -rw-r--r-- | nova/tests/test_compute.py | 59 | ||||
| -rw-r--r-- | nova/tests/test_localization.py | 3 | ||||
| -rw-r--r-- | nova/tests/test_misc.py | 43 | ||||
| -rw-r--r-- | nova/tests/test_virt.py | 2 | ||||
| -rw-r--r-- | nova/tests/xenapi/stubs.py | 7 |
10 files changed, 353 insertions, 51 deletions
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 75eade4d0..d931b6efb 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import datetime import json import random @@ -150,22 +151,23 @@ def stub_out_glance(stubs, initial_fixtures=None): for f in self.fixtures] def fake_get_images_detailed(self): - return self.fixtures + return copy.deepcopy(self.fixtures) def fake_get_image_meta(self, image_id): - for f in self.fixtures: - if f['id'] == image_id: - return f + image = self._find_image(image_id) + if image: + return copy.deepcopy(image) raise glance_exc.NotFound def fake_add_image(self, image_meta, data=None): + image_meta = copy.deepcopy(image_meta) id = ''.join(random.choice(string.letters) for _ in range(20)) image_meta['id'] = id self.fixtures.append(image_meta) return image_meta def fake_update_image(self, image_id, image_meta, data=None): - f = self.fake_get_image_meta(image_id) + f = self._find_image(image_id) if not f: raise glance_exc.NotFound @@ -173,7 +175,7 @@ def stub_out_glance(stubs, initial_fixtures=None): return f def fake_delete_image(self, image_id): - f = self.fake_get_image_meta(image_id) + f = self._find_image(image_id) if not f: raise glance_exc.NotFound @@ -182,6 +184,12 @@ def stub_out_glance(stubs, initial_fixtures=None): ##def fake_delete_all(self): ## self.fixtures = [] + def _find_image(self, image_id): + for f in self.fixtures: + if f['id'] == image_id: + return f + return None + GlanceClient = glance_client.Client fake = FakeGlanceClient(initial_fixtures) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index a674ccefe..feb32ed9f 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -189,13 +189,13 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): """Test of the OpenStack API /images application controller""" # Registered images at start of each test. - + now = datetime.datetime.utcnow() IMAGE_FIXTURES = [ {'id': '23g2ogk23k4hhkk4k42l', 'imageId': '23g2ogk23k4hhkk4k42l', 'name': 'public image #1', - 'created_at': str(datetime.datetime.utcnow()), - 'updated_at': str(datetime.datetime.utcnow()), + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), 'deleted_at': None, 'deleted': False, 'is_public': True, @@ -204,8 +204,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): {'id': 'slkduhfas73kkaskgdas', 'imageId': 'slkduhfas73kkaskgdas', 'name': 'public image #2', - 'created_at': str(datetime.datetime.utcnow()), - 'updated_at': str(datetime.datetime.utcnow()), + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), 'deleted_at': None, 'deleted': False, 'is_public': True, @@ -247,20 +247,20 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) res_dict = json.loads(res.body) - def _is_equivalent_subset(x, y): - if set(x) <= set(y): - for k, v in x.iteritems(): - if x[k] != y[k]: - if x[k] == 'active' and y[k] == 'available': - continue - return False - return True - return False - - for image in res_dict['images']: - for image_fixture in self.IMAGE_FIXTURES: - if _is_equivalent_subset(image, image_fixture): - break - else: - self.assertEquals(1, 2, "image %s not in fixtures!" % - str(image)) + for image in self.IMAGE_FIXTURES: + expected = { + 'id': abs(hash(image['imageId'])), + 'name': image['name'], + 'status': 'active', + } + self.assertTrue(expected in res_dict['images']) + + def test_show_image(self): + expected = self.IMAGE_FIXTURES[0] + id = abs(hash(expected['id'])) + expected_time = self.now.strftime('%Y-%m-%dT%H:%M:%SZ') + req = webob.Request.blank('/v1.0/images/%s' % id) + res = req.get_response(fakes.wsgi_app()) + actual = json.loads(res.body)['image'] + self.assertEqual(expected_time, actual['created_at']) + self.assertEqual(expected_time, actual['updated_at']) diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index efba2970f..3a804c649 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -524,16 +524,6 @@ class ServersTest(test.TestCase): req.body = json.dumps(body) res = req.get_response(fakes.wsgi_app()) - def test_server_resize(self): - body = dict(server=dict( - name='server_test', imageId=2, flavorId=2, metadata={}, - personality={})) - req = webob.Request.blank('/v1.0/servers/1/action') - req.method = 'POST' - req.content_type = 'application/json' - req.body = json.dumps(body) - res = req.get_response(fakes.wsgi_app()) - def test_delete_server_instance(self): req = webob.Request.blank('/v1.0/servers/1') req.method = 'DELETE' @@ -589,6 +579,18 @@ class ServersTest(test.TestCase): res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 400) + def test_resized_server_has_correct_status(self): + req = self.webreq('/1', 'GET', dict(resize=dict(flavorId=3))) + + def fake_migration_get(*args): + return {} + + self.stubs.Set(nova.db, 'migration_get_by_instance_and_status', + fake_migration_get) + res = req.get_response(fakes.wsgi_app()) + body = json.loads(res.body) + self.assertEqual(body['server']['status'], 'resize-confirm') + def test_confirm_resize_server(self): req = self.webreq('/1/action', 'POST', dict(confirmResize=None)) diff --git a/nova/tests/image/__init__.py b/nova/tests/image/__init__.py new file mode 100644 index 000000000..b94e2e54e --- /dev/null +++ b/nova/tests/image/__init__.py @@ -0,0 +1,16 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Openstack LLC. +# All Rights Reserved. +# +# 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. diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py new file mode 100644 index 000000000..f1f8504f3 --- /dev/null +++ b/nova/tests/image/test_glance.py @@ -0,0 +1,188 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Openstack LLC. +# All Rights Reserved. +# +# 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 datetime +import unittest + +from nova.image import glance + + +class StubGlanceClient(object): + + def __init__(self, images, add_response=None, update_response=None): + self.images = images + self.add_response = add_response + self.update_response = update_response + + def get_image_meta(self, id): + return self.images[id] + + def get_images_detailed(self): + return self.images.itervalues() + + def get_image(self, id): + return self.images[id], [] + + def add_image(self, metadata, data): + return self.add_response + + def update_image(self, image_id, metadata, data): + return self.update_response + + +class NullWriter(object): + + def write(self, *arg, **kwargs): + pass + + +class TestGlanceImageServiceDatetimes(unittest.TestCase): + + def setUp(self): + self.client = StubGlanceClient(None) + self.service = glance.GlanceImageService(self.client) + + def test_show_passes_through_to_client(self): + self.client.images = {'xyz': {'foo': 'bar'}} + self.assertEqual(self.service.show({}, 'xyz'), {'foo': 'bar'}) + + def test_detail_passes_through_to_client(self): + self.client.images = {1: {'foo': 'bar'}} + self.assertEqual(list(self.service.detail({})), [{'foo': 'bar'}]) + + def test_show_makes_create_datetimes(self): + create_time = datetime.datetime.utcnow() + self.client.images = {'xyz': { + 'id': "id", + 'name': "my awesome image", + 'created_at': create_time.isoformat(), + }} + actual = self.service.show({}, 'xyz') + self.assertEqual(actual['created_at'], create_time) + + def test_show_makes_update_datetimes(self): + update_time = datetime.datetime.utcnow() + self.client.images = {'abc': { + 'id': "id", + 'name': "my okay image", + 'updated_at': update_time.isoformat(), + }} + actual = self.service.show({}, 'abc') + self.assertEqual(actual['updated_at'], update_time) + + def test_show_makes_delete_datetimes(self): + delete_time = datetime.datetime.utcnow() + self.client.images = {'123': { + 'id': "123", + 'name': "my lame image", + 'deleted_at': delete_time.isoformat(), + }} + actual = self.service.show({}, '123') + self.assertEqual(actual['deleted_at'], delete_time) + + def test_show_handles_deleted_at_none(self): + self.client.images = {'747': { + 'id': "747", + 'name': "not deleted", + 'deleted_at': None, + }} + actual = self.service.show({}, '747') + self.assertEqual(actual['deleted_at'], None) + + def test_detail_handles_timestamps(self): + now = datetime.datetime.utcnow() + image1 = { + 'id': 1, + 'name': 'image 1', + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), + 'deleted_at': None, + } + image2 = { + 'id': 2, + 'name': 'image 2', + 'deleted_at': now.isoformat(), + } + self.client.images = {1: image1, 2: image2} + i1, i2 = self.service.detail({}) + self.assertEqual(i1['created_at'], now) + self.assertEqual(i1['updated_at'], now) + self.assertEqual(i1['deleted_at'], None) + self.assertEqual(i2['deleted_at'], now) + + def test_get_handles_timestamps(self): + now = datetime.datetime.utcnow() + self.client.images = {'abcd': { + 'id': 'abcd', + 'name': 'nifty image', + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), + 'deleted_at': now.isoformat(), + }} + actual = self.service.get({}, 'abcd', NullWriter()) + for attr in ('created_at', 'updated_at', 'deleted_at'): + self.assertEqual(actual[attr], now) + + def test_get_handles_deleted_at_none(self): + self.client.images = {'abcd': {'deleted_at': None}} + actual = self.service.get({}, 'abcd', NullWriter()) + self.assertEqual(actual['deleted_at'], None) + + def test_create_handles_timestamps(self): + now = datetime.datetime.utcnow() + self.client.add_response = { + 'id': 'abcd', + 'name': 'blah', + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), + 'deleted_at': now.isoformat(), + } + actual = self.service.create({}, {}) + for attr in ('created_at', 'updated_at', 'deleted_at'): + self.assertEqual(actual[attr], now) + + def test_create_handles_deleted_at_none(self): + self.client.add_response = { + 'id': 'abcd', + 'name': 'blah', + 'deleted_at': None, + } + actual = self.service.create({}, {}) + self.assertEqual(actual['deleted_at'], None) + + def test_update_handles_timestamps(self): + now = datetime.datetime.utcnow() + self.client.update_response = { + 'id': 'abcd', + 'name': 'blah', + 'created_at': now.isoformat(), + 'updated_at': now.isoformat(), + 'deleted_at': now.isoformat(), + } + actual = self.service.update({}, 'dummy_id', {}) + for attr in ('created_at', 'updated_at', 'deleted_at'): + self.assertEqual(actual[attr], now) + + def test_create_handles_deleted_at_none(self): + self.client.update_response = { + 'id': 'abcd', + 'name': 'blah', + 'deleted_at': None, + } + actual = self.service.update({}, 'dummy_id', {}) + self.assertEqual(actual['deleted_at'], None) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 3651f4cef..44d04a12f 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -82,6 +82,21 @@ class ComputeTestCase(test.TestCase): inst.update(params) return db.instance_create(self.context, inst)['id'] + def _create_instance_type(self, params={}): + """Create a test instance""" + context = self.context.elevated() + inst = {} + inst['name'] = 'm1.small' + inst['memory_mb'] = '1024' + inst['vcpus'] = '1' + inst['local_gb'] = '20' + inst['flavorid'] = '1' + inst['swap'] = '2048' + inst['rxtx_quota'] = 100 + inst['rxtx_cap'] = 200 + inst.update(params) + return db.instance_type_create(context, inst)['id'] + def _create_group(self): values = {'name': 'testgroup', 'description': 'testgroup', @@ -299,15 +314,53 @@ class ComputeTestCase(test.TestCase): """Ensure instance can be migrated/resized""" instance_id = self._create_instance() context = self.context.elevated() + self.compute.run_instance(self.context, instance_id) db.instance_update(self.context, instance_id, {'host': 'foo'}) - self.compute.prep_resize(context, instance_id) + self.compute.prep_resize(context, instance_id, 1) migration_ref = db.migration_get_by_instance_and_status(context, instance_id, 'pre-migrating') self.compute.resize_instance(context, instance_id, migration_ref['id']) self.compute.terminate_instance(context, instance_id) + def test_resize_invalid_flavor_fails(self): + """Ensure invalid flavors raise""" + instance_id = self._create_instance() + context = self.context.elevated() + self.compute.run_instance(self.context, instance_id) + + self.assertRaises(exception.NotFound, self.compute_api.resize, + context, instance_id, 200) + + self.compute.terminate_instance(context, instance_id) + + def test_resize_down_fails(self): + """Ensure resizing down raises and fails""" + context = self.context.elevated() + instance_id = self._create_instance() + + self.compute.run_instance(self.context, instance_id) + db.instance_update(self.context, instance_id, + {'instance_type': 'm1.xlarge'}) + + self.assertRaises(exception.ApiError, self.compute_api.resize, + context, instance_id, 1) + + self.compute.terminate_instance(context, instance_id) + + def test_resize_same_size_fails(self): + """Ensure invalid flavors raise""" + context = self.context.elevated() + instance_id = self._create_instance() + + self.compute.run_instance(self.context, instance_id) + + self.assertRaises(exception.ApiError, self.compute_api.resize, + context, instance_id, 1) + + self.compute.terminate_instance(context, instance_id) + def test_get_by_flavor_id(self): type = instance_types.get_by_flavor_id(1) self.assertEqual(type, 'm1.tiny') @@ -318,10 +371,8 @@ class ComputeTestCase(test.TestCase): instance_id = self._create_instance() self.compute.run_instance(self.context, instance_id) self.assertRaises(exception.Error, self.compute.prep_resize, - self.context, instance_id) + self.context, instance_id, 1) self.compute.terminate_instance(self.context, instance_id) - type = instance_types.get_by_flavor_id("1") - self.assertEqual(type, 'm1.tiny') def _setup_other_managers(self): self.volume_manager = utils.import_object(FLAGS.volume_manager) diff --git a/nova/tests/test_localization.py b/nova/tests/test_localization.py index 393d71038..a25809a79 100644 --- a/nova/tests/test_localization.py +++ b/nova/tests/test_localization.py @@ -21,9 +21,10 @@ import sys import unittest import nova +from nova import test -class LocalizationTestCase(unittest.TestCase): +class LocalizationTestCase(test.TestCase): def test_multiple_positional_format_placeholders(self): pat = re.compile("\W_\(") single_pat = re.compile("\W%\W") diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 1fbaf304f..4e17e1ce0 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -18,8 +18,12 @@ import errno import os import select +from eventlet import greenpool +from eventlet import greenthread + from nova import test -from nova.utils import parse_mailmap, str_dict_replace, synchronized +from nova import utils +from nova.utils import parse_mailmap, str_dict_replace class ProjectTestCase(test.TestCase): @@ -63,7 +67,7 @@ class ProjectTestCase(test.TestCase): class LockTestCase(test.TestCase): def test_synchronized_wrapped_function_metadata(self): - @synchronized('whatever') + @utils.synchronized('whatever') def foo(): """Bar""" pass @@ -72,11 +76,42 @@ class LockTestCase(test.TestCase): self.assertEquals(foo.__name__, 'foo', "Wrapped function's name " "got mangled") - def test_synchronized(self): + def test_synchronized_internally(self): + """We can lock across multiple green threads""" + saved_sem_num = len(utils._semaphores) + seen_threads = list() + + @utils.synchronized('testlock2', external=False) + def f(id): + for x in range(10): + seen_threads.append(id) + greenthread.sleep(0) + + threads = [] + pool = greenpool.GreenPool(10) + for i in range(10): + threads.append(pool.spawn(f, i)) + + for thread in threads: + thread.wait() + + self.assertEquals(len(seen_threads), 100) + # Looking at the seen threads, split it into chunks of 10, and verify + # that the last 9 match the first in each chunk. + for i in range(10): + for j in range(9): + self.assertEquals(seen_threads[i * 10], + seen_threads[i * 10 + 1 + j]) + + self.assertEqual(saved_sem_num, len(utils._semaphores), + "Semaphore leak detected") + + def test_synchronized_externally(self): + """We can lock across multiple processes""" rpipe1, wpipe1 = os.pipe() rpipe2, wpipe2 = os.pipe() - @synchronized('testlock') + @utils.synchronized('testlock1', external=True) def f(rpipe, wpipe): try: os.write(wpipe, "foo") diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index b214f5ce7..6bafac39f 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -77,13 +77,11 @@ class CacheConcurrencyTestCase(test.TestCase): eventlet.sleep(0) try: self.assertFalse(done2.ready()) - self.assertTrue('fname' in conn._image_sems) finally: wait1.send() done1.wait() eventlet.sleep(0) self.assertTrue(done2.ready()) - self.assertFalse('fname' in conn._image_sems) def test_different_fname_concurrency(self): """Ensures that two different fname caches are concurrent""" diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 0f559b7f9..fdf2b8f55 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -232,6 +232,9 @@ class FakeSessionForMigrationTests(fake.SessionBase): def VDI_get_by_uuid(*args): return 'hurr' + def VDI_resize_online(*args): + pass + def VM_start(self, _1, ref, _2, _3): vm = fake.get_record('VM', ref) if vm['power_state'] != 'Halted': @@ -244,7 +247,7 @@ class FakeSessionForMigrationTests(fake.SessionBase): def stub_out_migration_methods(stubs): def fake_get_snapshot(self, instance): - return 'foo', 'bar' + return 'vm_ref', dict(image='foo', snap='bar') @classmethod def fake_get_vdi(cls, session, vm_ref): @@ -253,7 +256,7 @@ def stub_out_migration_methods(stubs): vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) return vdi_ref, {'uuid': vdi_rec['uuid'], } - def fake_shutdown(self, inst, vm, method='clean'): + def fake_shutdown(self, inst, vm, hard=True): pass @classmethod |
