From 1ba2496c7c8ff6c717d4ce7395f56e414132b79c Mon Sep 17 00:00:00 2001 From: Andrew Melton Date: Mon, 12 Nov 2012 13:19:19 -0500 Subject: [PATCH] Adding two snapshot related task states The first, 'image_pending_upload', indicates that the snapshot of a given instance has been taken and it is being prepared for uploading to the image service. The second, 'image_uploading', indicates that the compute manager has initiated upload to the image service. Implements blueprint snapshot-task-states Change-Id: I256c5d21a1d23b87d2060cca99eb9839c5b89161 --- nova/tests/test_hypervapi.py | 34 +++++++- nova/tests/test_libvirt.py | 142 +++++++++++++++++++++++++++++--- nova/tests/test_virt_drivers.py | 6 +- nova/tests/test_vmwareapi.py | 18 +++- nova/tests/test_xenapi.py | 20 ++++- 5 files changed, 202 insertions(+), 18 deletions(-) diff --git a/nova/tests/test_hypervapi.py b/nova/tests/test_hypervapi.py index eae3c015..cab877da 100644 --- a/nova/tests/test_hypervapi.py +++ b/nova/tests/test_hypervapi.py @@ -26,6 +26,7 @@ import sys import uuid from nova.compute import power_state +from nova.compute import task_states from nova import context from nova import db from nova.image import glance @@ -36,6 +37,7 @@ from nova.tests.hyperv import db_fakes from nova.tests.hyperv import hypervutils from nova.tests.hyperv import mockproxy import nova.tests.image.fake as fake_image +from nova.tests import matchers from nova.virt.hyperv import constants from nova.virt.hyperv import driver as driver_hyperv from nova.virt.hyperv import vmutils @@ -407,27 +409,55 @@ class HyperVAPITestCase(basetestcase.BaseTestCase): self.assertTrue(self._fetched_image is None) def test_snapshot_with_update_failure(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self._spawn_instance(True) self._update_image_raise_exception = True snapshot_name = 'test_snapshot_' + str(uuid.uuid4()) self.assertRaises(vmutils.HyperVException, self._conn.snapshot, - self._context, self._instance_data, snapshot_name) + self._context, self._instance_data, snapshot_name, + func_call_matcher.call) + + # assert states changed in correct order + self.assertIsNone(func_call_matcher.match()) # assert VM snapshots have been removed self.assertEquals(self._hypervutils.get_vm_snapshots_count( self._instance_data["name"]), 0) def test_snapshot(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self._spawn_instance(True) snapshot_name = 'test_snapshot_' + str(uuid.uuid4()) - self._conn.snapshot(self._context, self._instance_data, snapshot_name) + self._conn.snapshot(self._context, self._instance_data, snapshot_name, + func_call_matcher.call) self.assertTrue(self._image_metadata and "disk_format" in self._image_metadata and self._image_metadata["disk_format"] == "vhd") + # assert states changed in correct order + self.assertIsNone(func_call_matcher.match()) + # assert VM snapshots have been removed self.assertEquals(self._hypervutils.get_vm_snapshots_count( self._instance_data["name"]), 0) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index fc8e0172..3943e8c4 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -32,6 +32,7 @@ from xml.dom import minidom from nova.api.ec2 import cloud from nova.compute import instance_types from nova.compute import power_state +from nova.compute import task_states from nova.compute import vm_mode from nova.compute import vm_states from nova import context @@ -1209,6 +1210,16 @@ class LibvirtConnTestCase(test.TestCase): self.assertEqual(devices, ['vda', 'vdb']) def test_snapshot_in_ami_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./') # Start test @@ -1238,15 +1249,28 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) + self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'ami') self.assertEquals(snapshot['name'], snapshot_name) def test_lxc_snapshot_in_ami_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./', libvirt_type='lxc') @@ -1277,15 +1301,27 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'ami') self.assertEquals(snapshot['name'], snapshot_name) def test_snapshot_in_raw_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./') # Start test @@ -1316,15 +1352,27 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'raw') self.assertEquals(snapshot['name'], snapshot_name) def test_lxc_snapshot_in_raw_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./', libvirt_type='lxc') @@ -1356,15 +1404,27 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'raw') self.assertEquals(snapshot['name'], snapshot_name) def test_snapshot_in_qcow2_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(snapshot_image_format='qcow2', libvirt_snapshots_directory='./') @@ -1391,15 +1451,27 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'qcow2') self.assertEquals(snapshot['name'], snapshot_name) def test_lxc_snapshot_in_qcow2_format(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(snapshot_image_format='qcow2', libvirt_snapshots_directory='./', libvirt_type='lxc') @@ -1427,15 +1499,27 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['disk_format'], 'qcow2') self.assertEquals(snapshot['name'], snapshot_name) def test_snapshot_no_image_architecture(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./') # Start test @@ -1465,14 +1549,26 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['name'], snapshot_name) def test_lxc_snapshot_no_image_architecture(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./', libvirt_type='lxc') @@ -1503,14 +1599,26 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['name'], snapshot_name) def test_snapshot_no_original_image(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./') # Start test @@ -1536,14 +1644,26 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['name'], snapshot_name) def test_lxc_snapshot_no_original_image(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + self.flags(libvirt_snapshots_directory='./', libvirt_type='lxc') @@ -1570,9 +1690,11 @@ class LibvirtConnTestCase(test.TestCase): self.mox.ReplayAll() conn = libvirt_driver.LibvirtDriver(False) - conn.snapshot(self.context, instance_ref, recv_meta['id']) + conn.snapshot(self.context, instance_ref, recv_meta['id'], + func_call_matcher.call) snapshot = image_service.show(context, recv_meta['id']) + self.assertIsNone(func_call_matcher.match()) self.assertEquals(snapshot['properties']['image_state'], 'available') self.assertEquals(snapshot['status'], 'active') self.assertEquals(snapshot['name'], snapshot_name) diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index b0e25d09..9d9ebcad 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -215,13 +215,15 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'}) self.assertRaises(exception.InstanceNotRunning, self.connection.snapshot, - self.ctxt, instance_ref, img_ref['id']) + self.ctxt, instance_ref, img_ref['id'], + lambda *args, **kwargs: None) @catch_notimplementederror def test_snapshot_running(self): img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'}) instance_ref, network_info = self._get_running_instance() - self.connection.snapshot(self.ctxt, instance_ref, img_ref['id']) + self.connection.snapshot(self.ctxt, instance_ref, img_ref['id'], + lambda *args, **kwargs: None) @catch_notimplementederror def test_reboot(self): diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py index 3a404a12..86b3a573 100644 --- a/nova/tests/test_vmwareapi.py +++ b/nova/tests/test_vmwareapi.py @@ -20,11 +20,13 @@ Test suite for VMWareAPI. """ from nova.compute import power_state +from nova.compute import task_states from nova import context from nova import db from nova import exception from nova import test import nova.tests.image.fake +from nova.tests import matchers from nova.tests.vmwareapi import db_fakes from nova.tests.vmwareapi import stubs from nova.virt.vmwareapi import driver @@ -159,17 +161,29 @@ class VMWareAPIVMTestCase(test.TestCase): self._check_vm_info(info, power_state.RUNNING) def test_snapshot(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) self._create_vm() info = self.conn.get_info({'name': 1}) self._check_vm_info(info, power_state.RUNNING) - self.conn.snapshot(self.context, self.instance, "Test-Snapshot") + self.conn.snapshot(self.context, self.instance, "Test-Snapshot", + func_call_matcher.call) info = self.conn.get_info({'name': 1}) self._check_vm_info(info, power_state.RUNNING) + self.assertIsNone(func_call_matcher.match()) def test_snapshot_non_existent(self): self._create_instance_in_the_db() self.assertRaises(exception.InstanceNotFound, self.conn.snapshot, - self.context, self.instance, "Test-Snapshot") + self.context, self.instance, "Test-Snapshot", + lambda *args, **kwargs: None) def test_reboot(self): self._create_vm() diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 3ca69dc4..8b57dfef 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -397,6 +397,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): self.assertThat(fake_diagnostics, matchers.DictMatches(expected)) def test_instance_snapshot_fails_with_no_primary_vdi(self): + def create_bad_vbd(session, vm_ref, vdi_ref, userdevice, vbd_type='disk', read_only=False, bootable=False, osvol=False): @@ -417,9 +418,20 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): image_id = "my_snapshot_id" self.assertRaises(exception.NovaException, self.conn.snapshot, - self.context, instance, image_id) + self.context, instance, image_id, + lambda *args, **kwargs: None) def test_instance_snapshot(self): + expected_calls = [ + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_PENDING_UPLOAD}}, + {'args': (), + 'kwargs': + {'task_state': task_states.IMAGE_UPLOADING, + 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}] + func_call_matcher = matchers.FunctionCallMatcher(expected_calls) + stubs.stubout_instance_snapshot(self.stubs) stubs.stubout_is_snapshot(self.stubs) # Stubbing out firewall driver as previous stub sets alters @@ -428,7 +440,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): instance = self._create_instance() image_id = "my_snapshot_id" - self.conn.snapshot(self.context, instance, image_id) + self.conn.snapshot(self.context, instance, image_id, + func_call_matcher.call) # Ensure VM was torn down vm_labels = [] @@ -447,6 +460,9 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): self.assertEquals(vbd_labels, [instance['name']]) + # Ensure task states changed in correct order + self.assertIsNone(func_call_matcher.match()) + # Ensure VDIs were torn down for vdi_ref in xenapi_fake.get_all('VDI'): vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)