From 6c4059f20c85e9bc013a340de167151e7b5fa8c4 Mon Sep 17 00:00:00 2001 From: Mike Scherbakov Date: Wed, 11 May 2011 03:24:02 +0400 Subject: Bugfix #780784. KeyError when creating custom image. --- Authors | 1 + nova/virt/libvirt_conn.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Authors b/Authors index 60e1d2dad..72eb0b6ae 100644 --- a/Authors +++ b/Authors @@ -54,6 +54,7 @@ Mark Washenberger Masanori Itoh Matt Dietz Michael Gundlach +Mike Scherbakov Monsyne Dragon Monty Taylor MORITA Kazutaka diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 9780c69a6..71cedae54 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -456,7 +456,8 @@ class LibvirtConnection(driver.ComputeDriver): 'container_format': base['container_format'], 'is_public': False, 'name': '%s.%s' % (base['name'], image_id), - 'properties': {'architecture': base['architecture'], + 'properties': {'architecture': + base['properties']['architecture'], 'kernel_id': instance['kernel_id'], 'image_location': 'snapshot', 'image_state': 'available', -- cgit From f889f6c8a430d6411a81270a68025a27781b09a2 Mon Sep 17 00:00:00 2001 From: Mike Scherbakov Date: Sun, 15 May 2011 14:15:37 +0400 Subject: Unit test for snapshotting (creating custom image). --- nova/image/fake.py | 33 +++++++++++++++++++++------- nova/tests/test_virt.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/nova/image/fake.py b/nova/image/fake.py index b400b2adb..4bf25d9af 100644 --- a/nova/image/fake.py +++ b/nova/image/fake.py @@ -19,6 +19,7 @@ import copy import datetime +import random from nova import exception from nova import flags @@ -32,7 +33,7 @@ LOG = logging.getLogger('nova.image.fake') FLAGS = flags.FLAGS -class FakeImageService(service.BaseImageService): +class _FakeImageService(service.BaseImageService): """Mock (fake) image service for unit testing.""" def __init__(self): @@ -48,9 +49,10 @@ class FakeImageService(service.BaseImageService): 'container_format': 'ami', 'disk_format': 'raw', 'properties': {'kernel_id': FLAGS.null_kernel, - 'ramdisk_id': FLAGS.null_kernel}} + 'ramdisk_id': FLAGS.null_kernel, + 'architecture': 'x86_64'}} self.create(None, image) - super(FakeImageService, self).__init__() + super(_FakeImageService, self).__init__() def index(self, context): """Returns list of images.""" @@ -74,19 +76,28 @@ class FakeImageService(service.BaseImageService): image_id, self.images) raise exception.ImageNotFound(image_id=image_id) - def create(self, context, data): + def create(self, context, metadata, data=None): """Store the image data and return the new image id. :raises: Duplicate if the image already exist. """ - image_id = int(data['id']) + #image_id = int(metadata['id']) + # metadata['id'] may not exists, and since image_id is + # randomly generated in local.py, let us do the same here + try: + image_id = int(metadata['id']) + except: + image_id = random.randint(0, 2 ** 31 - 1) + if self.images.get(image_id): raise exception.Duplicate() - self.images[image_id] = copy.deepcopy(data) + metadata['id'] = image_id + self.images[image_id] = copy.deepcopy(metadata) + return self.images[image_id] - def update(self, context, image_id, data): + def update(self, context, image_id, metadata, data=None): """Replace the contents of the given image with the new data. :raises: ImageNotFound if the image does not exist. @@ -95,7 +106,7 @@ class FakeImageService(service.BaseImageService): image_id = int(image_id) if not self.images.get(image_id): raise exception.ImageNotFound(image_id=image_id) - self.images[image_id] = copy.deepcopy(data) + self.images[image_id] = copy.deepcopy(metadata) def delete(self, context, image_id): """Delete the given image. @@ -111,3 +122,9 @@ class FakeImageService(service.BaseImageService): def delete_all(self): """Clears out all images.""" self.images.clear() + +_fakeImageService = _FakeImageService() + + +def FakeImageService(): + return _fakeImageService diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 1311ba361..eb238e871 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -159,6 +159,7 @@ class LibvirtConnTestCase(test.TestCase): 'vcpus': 2, 'project_id': 'fake', 'bridge': 'br101', + 'image_id': '123456', 'instance_type_id': '5'} # m1.small def lazy_load_library_exists(self): @@ -279,6 +280,62 @@ class LibvirtConnTestCase(test.TestCase): instance_data = dict(self.test_instance) self._check_xml_and_container(instance_data) + def test_snapshot(self): + FLAGS.image_service = 'nova.image.fake.FakeImageService' + + # Only file-based instance storages are supported at the moment + test_xml = """ + + + + + + + + """ + + class FakeVirtDomain(object): + + def __init__(self): + pass + + def snapshotCreateXML(self, *args): + return None + + def XMLDesc(self, *args): + return test_xml + + def fake_lookup(instance_name): + if instance_name == instance_ref.name: + return FakeVirtDomain() + + def fake_execute(*args): + # Touch filename to pass 'with open(out_path)' + open(args[-1], "a").close() + + # Start test + image_service = utils.import_object(FLAGS.image_service) + + # Assuming that base image already exists in image_service + instance_ref = db.instance_create(self.context, self.test_instance) + properties = {'instance_id': instance_ref['id'], + 'user_id': str(self.context.user_id)} + sent_meta = {'name': 'test-snap', 'is_public': False, + 'properties': properties} + # Create new image. It will be updated in snapshot method + # To work with it from snapshot, the single image_service is needed + recv_meta = image_service.create(context, sent_meta) + + self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn') + libvirt_conn.LibvirtConnection._conn.lookupByName = fake_lookup + self.mox.StubOutWithMock(libvirt_conn.utils, 'execute') + libvirt_conn.utils.execute = fake_execute + + self.mox.ReplayAll() + + conn = libvirt_conn.LibvirtConnection(False) + conn.snapshot(instance_ref, recv_meta['id']) + def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _create_network_info(2) -- cgit From 818c2424a0547882fe6bdfe6613ee66a248d91db Mon Sep 17 00:00:00 2001 From: Mike Scherbakov Date: Sun, 15 May 2011 15:11:54 +0400 Subject: Define image state during snapshotting. Name snapshot to the name provided, not generate. --- nova/compute/api.py | 5 +++-- nova/tests/test_virt.py | 10 ++++++++-- nova/virt/libvirt_conn.py | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/nova/compute/api.py b/nova/compute/api.py index 63884be97..971c0732f 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -500,9 +500,10 @@ class API(base.Base): """ properties = {'instance_id': str(instance_id), - 'user_id': str(context.user_id)} + 'user_id': str(context.user_id), + 'image_state': 'creating'} sent_meta = {'name': name, 'is_public': False, - 'properties': properties} + 'status': 'creating', 'properties': properties} recv_meta = self.image_service.create(context, sent_meta) params = {'image_id': recv_meta['id']} self._cast_compute_message('snapshot_instance', context, instance_id, diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index eb238e871..c4fcc21cc 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -320,8 +320,9 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) properties = {'instance_id': instance_ref['id'], 'user_id': str(self.context.user_id)} - sent_meta = {'name': 'test-snap', 'is_public': False, - 'properties': properties} + snapshot_name = 'test-snap' + sent_meta = {'name': snapshot_name, 'is_public': False, + 'status': 'creating', 'properties': properties} # Create new image. It will be updated in snapshot method # To work with it from snapshot, the single image_service is needed recv_meta = image_service.create(context, sent_meta) @@ -336,6 +337,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_conn.LibvirtConnection(False) conn.snapshot(instance_ref, recv_meta['id']) + snapshot = image_service.show(context, recv_meta['id']) + self.assertEquals(snapshot['properties']['image_state'], 'available') + self.assertEquals(snapshot['status'], 'active') + self.assertEquals(snapshot['name'], snapshot_name) + def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _create_network_info(2) diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 71cedae54..92d580314 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -451,11 +451,13 @@ class LibvirtConnection(driver.ComputeDriver): elevated = context.get_admin_context() base = image_service.show(elevated, instance['image_id']) + snapshot = image_service.show(elevated, image_id) metadata = {'disk_format': base['disk_format'], 'container_format': base['container_format'], 'is_public': False, - 'name': '%s.%s' % (base['name'], image_id), + 'status': 'active', + 'name': snapshot['name'], 'properties': {'architecture': base['properties']['architecture'], 'kernel_id': instance['kernel_id'], -- cgit From b39b0e66f16d49890189c63fba528734ef476068 Mon Sep 17 00:00:00 2001 From: Mike Scherbakov Date: Thu, 2 Jun 2011 10:29:58 +0400 Subject: Refactored after review, fixed merge. --- nova/image/fake.py | 5 +---- nova/tests/test_libvirt.py | 10 +++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/nova/image/fake.py b/nova/image/fake.py index 4bf25d9af..019d683f4 100644 --- a/nova/image/fake.py +++ b/nova/image/fake.py @@ -82,12 +82,9 @@ class _FakeImageService(service.BaseImageService): :raises: Duplicate if the image already exist. """ - #image_id = int(metadata['id']) - # metadata['id'] may not exists, and since image_id is - # randomly generated in local.py, let us do the same here try: image_id = int(metadata['id']) - except: + except KeyError: image_id = random.randint(0, 2 ** 31 - 1) if self.images.get(image_id): diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index d9316ab4f..d008a149e 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -328,14 +328,14 @@ class LibvirtConnTestCase(test.TestCase): # To work with it from snapshot, the single image_service is needed recv_meta = image_service.create(context, sent_meta) - self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn') - libvirt_conn.LibvirtConnection._conn.lookupByName = fake_lookup - self.mox.StubOutWithMock(libvirt_conn.utils, 'execute') - libvirt_conn.utils.execute = fake_execute + self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') + connection.LibvirtConnection._conn.lookupByName = fake_lookup + self.mox.StubOutWithMock(connection.utils, 'execute') + connection.utils.execute = fake_execute self.mox.ReplayAll() - conn = libvirt_conn.LibvirtConnection(False) + conn = connection.LibvirtConnection(False) conn.snapshot(instance_ref, recv_meta['id']) snapshot = image_service.show(context, recv_meta['id']) -- cgit