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
This commit is contained in:
@@ -1388,16 +1388,21 @@ class ComputeManager(manager.SchedulerDependentManager):
|
|||||||
self._notify_about_instance_usage(
|
self._notify_about_instance_usage(
|
||||||
context, instance, "snapshot.start")
|
context, instance, "snapshot.start")
|
||||||
|
|
||||||
self.driver.snapshot(context, instance, image_id)
|
|
||||||
|
|
||||||
if image_type == 'snapshot':
|
if image_type == 'snapshot':
|
||||||
expected_task_state = task_states.IMAGE_SNAPSHOT
|
expected_task_state = task_states.IMAGE_SNAPSHOT
|
||||||
|
|
||||||
elif image_type == 'backup':
|
elif image_type == 'backup':
|
||||||
expected_task_state = task_states.IMAGE_BACKUP
|
expected_task_state = task_states.IMAGE_BACKUP
|
||||||
|
|
||||||
|
def update_task_state(task_state, expected_state=expected_task_state):
|
||||||
|
self._instance_update(context, instance['uuid'],
|
||||||
|
task_state=task_state,
|
||||||
|
expected_task_state=expected_state)
|
||||||
|
|
||||||
|
self.driver.snapshot(context, instance, image_id, update_task_state)
|
||||||
|
|
||||||
self._instance_update(context, instance['uuid'], task_state=None,
|
self._instance_update(context, instance['uuid'], task_state=None,
|
||||||
expected_task_state=expected_task_state)
|
expected_task_state=task_states.IMAGE_UPLOADING)
|
||||||
|
|
||||||
if image_type == 'snapshot' and rotation:
|
if image_type == 'snapshot' and rotation:
|
||||||
raise exception.ImageRotationNotAllowed()
|
raise exception.ImageRotationNotAllowed()
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ SPAWNING = 'spawning'
|
|||||||
|
|
||||||
# possible task states during snapshot()
|
# possible task states during snapshot()
|
||||||
IMAGE_SNAPSHOT = 'image_snapshot'
|
IMAGE_SNAPSHOT = 'image_snapshot'
|
||||||
|
IMAGE_PENDING_UPLOAD = 'image_pending_upload'
|
||||||
|
IMAGE_UPLOADING = 'image_uploading'
|
||||||
|
|
||||||
# possible task states during backup()
|
# possible task states during backup()
|
||||||
IMAGE_BACKUP = 'image_backup'
|
IMAGE_BACKUP = 'image_backup'
|
||||||
|
|||||||
@@ -198,6 +198,21 @@ class IsSubDictOf(object):
|
|||||||
return SubDictMismatch(k, sub_value, super_value)
|
return SubDictMismatch(k, sub_value, super_value)
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionCallMatcher(object):
|
||||||
|
|
||||||
|
def __init__(self, expected_func_calls):
|
||||||
|
self.expected_func_calls = expected_func_calls
|
||||||
|
self.actual_func_calls = []
|
||||||
|
|
||||||
|
def call(self, *args, **kwargs):
|
||||||
|
func_call = {'args': args, 'kwargs': kwargs}
|
||||||
|
self.actual_func_calls.append(func_call)
|
||||||
|
|
||||||
|
def match(self):
|
||||||
|
dict_list_matcher = DictListMatches(self.expected_func_calls)
|
||||||
|
return dict_list_matcher.match(self.actual_func_calls)
|
||||||
|
|
||||||
|
|
||||||
class XMLMismatch(object):
|
class XMLMismatch(object):
|
||||||
"""Superclass for XML mismatch."""
|
"""Superclass for XML mismatch."""
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import sys
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova.image import glance
|
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 hypervutils
|
||||||
from nova.tests.hyperv import mockproxy
|
from nova.tests.hyperv import mockproxy
|
||||||
import nova.tests.image.fake as fake_image
|
import nova.tests.image.fake as fake_image
|
||||||
|
from nova.tests import matchers
|
||||||
from nova.virt.hyperv import constants
|
from nova.virt.hyperv import constants
|
||||||
from nova.virt.hyperv import driver as driver_hyperv
|
from nova.virt.hyperv import driver as driver_hyperv
|
||||||
from nova.virt.hyperv import vmutils
|
from nova.virt.hyperv import vmutils
|
||||||
@@ -407,27 +409,55 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
|
|||||||
self.assertTrue(self._fetched_image is None)
|
self.assertTrue(self._fetched_image is None)
|
||||||
|
|
||||||
def test_snapshot_with_update_failure(self):
|
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._spawn_instance(True)
|
||||||
|
|
||||||
self._update_image_raise_exception = True
|
self._update_image_raise_exception = True
|
||||||
snapshot_name = 'test_snapshot_' + str(uuid.uuid4())
|
snapshot_name = 'test_snapshot_' + str(uuid.uuid4())
|
||||||
self.assertRaises(vmutils.HyperVException, self._conn.snapshot,
|
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
|
# assert VM snapshots have been removed
|
||||||
self.assertEquals(self._hypervutils.get_vm_snapshots_count(
|
self.assertEquals(self._hypervutils.get_vm_snapshots_count(
|
||||||
self._instance_data["name"]), 0)
|
self._instance_data["name"]), 0)
|
||||||
|
|
||||||
def test_snapshot(self):
|
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)
|
self._spawn_instance(True)
|
||||||
|
|
||||||
snapshot_name = 'test_snapshot_' + str(uuid.uuid4())
|
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
|
self.assertTrue(self._image_metadata and
|
||||||
"disk_format" in self._image_metadata and
|
"disk_format" in self._image_metadata and
|
||||||
self._image_metadata["disk_format"] == "vhd")
|
self._image_metadata["disk_format"] == "vhd")
|
||||||
|
|
||||||
|
# assert states changed in correct order
|
||||||
|
self.assertIsNone(func_call_matcher.match())
|
||||||
|
|
||||||
# assert VM snapshots have been removed
|
# assert VM snapshots have been removed
|
||||||
self.assertEquals(self._hypervutils.get_vm_snapshots_count(
|
self.assertEquals(self._hypervutils.get_vm_snapshots_count(
|
||||||
self._instance_data["name"]), 0)
|
self._instance_data["name"]), 0)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from xml.dom import minidom
|
|||||||
from nova.api.ec2 import cloud
|
from nova.api.ec2 import cloud
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova.compute import vm_mode
|
from nova.compute import vm_mode
|
||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
from nova import context
|
from nova import context
|
||||||
@@ -1209,6 +1210,16 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.assertEqual(devices, ['vda', 'vdb'])
|
self.assertEqual(devices, ['vda', 'vdb'])
|
||||||
|
|
||||||
def test_snapshot_in_ami_format(self):
|
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='./')
|
self.flags(libvirt_snapshots_directory='./')
|
||||||
|
|
||||||
# Start test
|
# Start test
|
||||||
@@ -1238,15 +1249,28 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['properties']['image_state'], 'available')
|
||||||
self.assertEquals(snapshot['status'], 'active')
|
self.assertEquals(snapshot['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'ami')
|
self.assertEquals(snapshot['disk_format'], 'ami')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_lxc_snapshot_in_ami_format(self):
|
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='./',
|
self.flags(libvirt_snapshots_directory='./',
|
||||||
libvirt_type='lxc')
|
libvirt_type='lxc')
|
||||||
|
|
||||||
@@ -1277,15 +1301,27 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'ami')
|
self.assertEquals(snapshot['disk_format'], 'ami')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_snapshot_in_raw_format(self):
|
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='./')
|
self.flags(libvirt_snapshots_directory='./')
|
||||||
|
|
||||||
# Start test
|
# Start test
|
||||||
@@ -1316,15 +1352,27 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'raw')
|
self.assertEquals(snapshot['disk_format'], 'raw')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_lxc_snapshot_in_raw_format(self):
|
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='./',
|
self.flags(libvirt_snapshots_directory='./',
|
||||||
libvirt_type='lxc')
|
libvirt_type='lxc')
|
||||||
|
|
||||||
@@ -1356,15 +1404,27 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'raw')
|
self.assertEquals(snapshot['disk_format'], 'raw')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_snapshot_in_qcow2_format(self):
|
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',
|
self.flags(snapshot_image_format='qcow2',
|
||||||
libvirt_snapshots_directory='./')
|
libvirt_snapshots_directory='./')
|
||||||
|
|
||||||
@@ -1391,15 +1451,27 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'qcow2')
|
self.assertEquals(snapshot['disk_format'], 'qcow2')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_lxc_snapshot_in_qcow2_format(self):
|
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',
|
self.flags(snapshot_image_format='qcow2',
|
||||||
libvirt_snapshots_directory='./',
|
libvirt_snapshots_directory='./',
|
||||||
libvirt_type='lxc')
|
libvirt_type='lxc')
|
||||||
@@ -1427,15 +1499,27 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['disk_format'], 'qcow2')
|
self.assertEquals(snapshot['disk_format'], 'qcow2')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_snapshot_no_image_architecture(self):
|
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='./')
|
self.flags(libvirt_snapshots_directory='./')
|
||||||
|
|
||||||
# Start test
|
# Start test
|
||||||
@@ -1465,14 +1549,26 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_lxc_snapshot_no_image_architecture(self):
|
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='./',
|
self.flags(libvirt_snapshots_directory='./',
|
||||||
libvirt_type='lxc')
|
libvirt_type='lxc')
|
||||||
|
|
||||||
@@ -1503,14 +1599,26 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_snapshot_no_original_image(self):
|
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='./')
|
self.flags(libvirt_snapshots_directory='./')
|
||||||
|
|
||||||
# Start test
|
# Start test
|
||||||
@@ -1536,14 +1644,26 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|
||||||
def test_lxc_snapshot_no_original_image(self):
|
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='./',
|
self.flags(libvirt_snapshots_directory='./',
|
||||||
libvirt_type='lxc')
|
libvirt_type='lxc')
|
||||||
|
|
||||||
@@ -1570,9 +1690,11 @@ class LibvirtConnTestCase(test.TestCase):
|
|||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
conn = libvirt_driver.LibvirtDriver(False)
|
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'])
|
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['status'], 'active')
|
||||||
self.assertEquals(snapshot['name'], snapshot_name)
|
self.assertEquals(snapshot['name'], snapshot_name)
|
||||||
|
|||||||
@@ -215,13 +215,15 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
|
|||||||
img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
|
img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
|
||||||
self.assertRaises(exception.InstanceNotRunning,
|
self.assertRaises(exception.InstanceNotRunning,
|
||||||
self.connection.snapshot,
|
self.connection.snapshot,
|
||||||
self.ctxt, instance_ref, img_ref['id'])
|
self.ctxt, instance_ref, img_ref['id'],
|
||||||
|
lambda *args, **kwargs: None)
|
||||||
|
|
||||||
@catch_notimplementederror
|
@catch_notimplementederror
|
||||||
def test_snapshot_running(self):
|
def test_snapshot_running(self):
|
||||||
img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
|
img_ref = self.image_service.create(self.ctxt, {'name': 'snap-1'})
|
||||||
instance_ref, network_info = self._get_running_instance()
|
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
|
@catch_notimplementederror
|
||||||
def test_reboot(self):
|
def test_reboot(self):
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ Test suite for VMWareAPI.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import test
|
from nova import test
|
||||||
import nova.tests.image.fake
|
import nova.tests.image.fake
|
||||||
|
from nova.tests import matchers
|
||||||
from nova.tests.vmwareapi import db_fakes
|
from nova.tests.vmwareapi import db_fakes
|
||||||
from nova.tests.vmwareapi import stubs
|
from nova.tests.vmwareapi import stubs
|
||||||
from nova.virt.vmwareapi import driver
|
from nova.virt.vmwareapi import driver
|
||||||
@@ -159,17 +161,29 @@ class VMWareAPIVMTestCase(test.TestCase):
|
|||||||
self._check_vm_info(info, power_state.RUNNING)
|
self._check_vm_info(info, power_state.RUNNING)
|
||||||
|
|
||||||
def test_snapshot(self):
|
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()
|
self._create_vm()
|
||||||
info = self.conn.get_info({'name': 1})
|
info = self.conn.get_info({'name': 1})
|
||||||
self._check_vm_info(info, power_state.RUNNING)
|
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})
|
info = self.conn.get_info({'name': 1})
|
||||||
self._check_vm_info(info, power_state.RUNNING)
|
self._check_vm_info(info, power_state.RUNNING)
|
||||||
|
self.assertIsNone(func_call_matcher.match())
|
||||||
|
|
||||||
def test_snapshot_non_existent(self):
|
def test_snapshot_non_existent(self):
|
||||||
self._create_instance_in_the_db()
|
self._create_instance_in_the_db()
|
||||||
self.assertRaises(exception.InstanceNotFound, self.conn.snapshot,
|
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):
|
def test_reboot(self):
|
||||||
self._create_vm()
|
self._create_vm()
|
||||||
|
|||||||
@@ -397,6 +397,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
|
|||||||
self.assertThat(fake_diagnostics, matchers.DictMatches(expected))
|
self.assertThat(fake_diagnostics, matchers.DictMatches(expected))
|
||||||
|
|
||||||
def test_instance_snapshot_fails_with_no_primary_vdi(self):
|
def test_instance_snapshot_fails_with_no_primary_vdi(self):
|
||||||
|
|
||||||
def create_bad_vbd(session, vm_ref, vdi_ref, userdevice,
|
def create_bad_vbd(session, vm_ref, vdi_ref, userdevice,
|
||||||
vbd_type='disk', read_only=False, bootable=False,
|
vbd_type='disk', read_only=False, bootable=False,
|
||||||
osvol=False):
|
osvol=False):
|
||||||
@@ -417,9 +418,20 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
|
|||||||
|
|
||||||
image_id = "my_snapshot_id"
|
image_id = "my_snapshot_id"
|
||||||
self.assertRaises(exception.NovaException, self.conn.snapshot,
|
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):
|
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_instance_snapshot(self.stubs)
|
||||||
stubs.stubout_is_snapshot(self.stubs)
|
stubs.stubout_is_snapshot(self.stubs)
|
||||||
# Stubbing out firewall driver as previous stub sets alters
|
# Stubbing out firewall driver as previous stub sets alters
|
||||||
@@ -428,7 +440,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
|
|||||||
instance = self._create_instance()
|
instance = self._create_instance()
|
||||||
|
|
||||||
image_id = "my_snapshot_id"
|
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
|
# Ensure VM was torn down
|
||||||
vm_labels = []
|
vm_labels = []
|
||||||
@@ -447,6 +460,9 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
|
|||||||
|
|
||||||
self.assertEquals(vbd_labels, [instance['name']])
|
self.assertEquals(vbd_labels, [instance['name']])
|
||||||
|
|
||||||
|
# Ensure task states changed in correct order
|
||||||
|
self.assertIsNone(func_call_matcher.match())
|
||||||
|
|
||||||
# Ensure VDIs were torn down
|
# Ensure VDIs were torn down
|
||||||
for vdi_ref in xenapi_fake.get_all('VDI'):
|
for vdi_ref in xenapi_fake.get_all('VDI'):
|
||||||
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
|
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ class ComputeDriver(object):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def snapshot(self, context, instance, image_id):
|
def snapshot(self, context, instance, image_id, update_task_state):
|
||||||
"""
|
"""
|
||||||
Snapshots the specified instance.
|
Snapshots the specified instance.
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ semantics of real hypervisor connections.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import db
|
from nova import db
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
@@ -122,9 +123,10 @@ class FakeDriver(driver.ComputeDriver):
|
|||||||
fake_instance = FakeInstance(name, state)
|
fake_instance = FakeInstance(name, state)
|
||||||
self.instances[name] = fake_instance
|
self.instances[name] = fake_instance
|
||||||
|
|
||||||
def snapshot(self, context, instance, name):
|
def snapshot(self, context, instance, name, update_task_state):
|
||||||
if not instance['name'] in self.instances:
|
if not instance['name'] in self.instances:
|
||||||
raise exception.InstanceNotRunning(instance_id=instance['uuid'])
|
raise exception.InstanceNotRunning(instance_id=instance['uuid'])
|
||||||
|
update_task_state(task_state=task_states.IMAGE_UPLOADING)
|
||||||
|
|
||||||
def reboot(self, instance, network_info, reboot_type,
|
def reboot(self, instance, network_info, reboot_type,
|
||||||
block_device_info=None):
|
block_device_info=None):
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ class HyperVDriver(driver.ComputeDriver):
|
|||||||
def host_power_action(self, host, action):
|
def host_power_action(self, host, action):
|
||||||
return self._hostops.host_power_action(host, action)
|
return self._hostops.host_power_action(host, action)
|
||||||
|
|
||||||
def snapshot(self, context, instance, name):
|
def snapshot(self, context, instance, name, update_task_state):
|
||||||
self._snapshotops.snapshot(context, instance, name)
|
self._snapshotops.snapshot(context, instance, name, update_task_state)
|
||||||
|
|
||||||
def pause(self, instance):
|
def pause(self, instance):
|
||||||
self._vmops.pause(instance)
|
self._vmops.pause(instance)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.image import glance
|
from nova.image import glance
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
@@ -45,7 +46,7 @@ class SnapshotOps(baseops.BaseOps):
|
|||||||
super(SnapshotOps, self).__init__()
|
super(SnapshotOps, self).__init__()
|
||||||
self._vmutils = vmutils.VMUtils()
|
self._vmutils = vmutils.VMUtils()
|
||||||
|
|
||||||
def snapshot(self, context, instance, name):
|
def snapshot(self, context, instance, name, update_task_state):
|
||||||
"""Create snapshot from a running VM instance."""
|
"""Create snapshot from a running VM instance."""
|
||||||
instance_name = instance["name"]
|
instance_name = instance["name"]
|
||||||
vm = self._vmutils.lookup(self._conn, instance_name)
|
vm = self._vmutils.lookup(self._conn, instance_name)
|
||||||
@@ -70,6 +71,8 @@ class SnapshotOps(baseops.BaseOps):
|
|||||||
raise vmutils.HyperVException(
|
raise vmutils.HyperVException(
|
||||||
_('Failed to create snapshot for VM %s') %
|
_('Failed to create snapshot for VM %s') %
|
||||||
instance_name)
|
instance_name)
|
||||||
|
else:
|
||||||
|
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
|
|
||||||
export_folder = None
|
export_folder = None
|
||||||
f = None
|
f = None
|
||||||
@@ -164,6 +167,8 @@ class SnapshotOps(baseops.BaseOps):
|
|||||||
_("Updating Glance image %(image_id)s with content from "
|
_("Updating Glance image %(image_id)s with content from "
|
||||||
"merged disk %(image_vhd_path)s"),
|
"merged disk %(image_vhd_path)s"),
|
||||||
locals())
|
locals())
|
||||||
|
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||||
|
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
glance_image_service.update(context, image_id, image_metadata, f)
|
glance_image_service.update(context, image_id, image_metadata, f)
|
||||||
|
|
||||||
LOG.debug(_("Snapshot image %(image_id)s updated for VM "
|
LOG.debug(_("Snapshot image %(image_id)s updated for VM "
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ from xml.dom import minidom
|
|||||||
from nova.api.metadata import base as instance_metadata
|
from nova.api.metadata import base as instance_metadata
|
||||||
from nova import block_device
|
from nova import block_device
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova.compute import vm_mode
|
from nova.compute import vm_mode
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
@@ -736,7 +737,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
mount_device)
|
mount_device)
|
||||||
|
|
||||||
@exception.wrap_exception()
|
@exception.wrap_exception()
|
||||||
def snapshot(self, context, instance, image_href):
|
def snapshot(self, context, instance, image_href, update_task_state):
|
||||||
"""Create snapshot from a running VM instance.
|
"""Create snapshot from a running VM instance.
|
||||||
|
|
||||||
This command only works with qemu 0.14+
|
This command only works with qemu 0.14+
|
||||||
@@ -804,6 +805,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
image_type=source_format)
|
image_type=source_format)
|
||||||
|
|
||||||
snapshot.create()
|
snapshot.create()
|
||||||
|
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
|
|
||||||
# Export the snapshot to a raw image
|
# Export the snapshot to a raw image
|
||||||
snapshot_directory = CONF.libvirt_snapshots_directory
|
snapshot_directory = CONF.libvirt_snapshots_directory
|
||||||
@@ -821,6 +823,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
self._create_domain(domain=virt_dom)
|
self._create_domain(domain=virt_dom)
|
||||||
|
|
||||||
# Upload that image to the image service
|
# Upload that image to the image service
|
||||||
|
|
||||||
|
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||||
|
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
with libvirt_utils.file_open(out_path) as image_file:
|
with libvirt_utils.file_open(out_path) as image_file:
|
||||||
image_service.update(context,
|
image_service.update(context,
|
||||||
image_href,
|
image_href,
|
||||||
|
|||||||
@@ -130,9 +130,9 @@ class VMWareESXDriver(driver.ComputeDriver):
|
|||||||
"""Create VM instance."""
|
"""Create VM instance."""
|
||||||
self._vmops.spawn(context, instance, image_meta, network_info)
|
self._vmops.spawn(context, instance, image_meta, network_info)
|
||||||
|
|
||||||
def snapshot(self, context, instance, name):
|
def snapshot(self, context, instance, name, update_task_state):
|
||||||
"""Create snapshot from a running VM instance."""
|
"""Create snapshot from a running VM instance."""
|
||||||
self._vmops.snapshot(context, instance, name)
|
self._vmops.snapshot(context, instance, name, update_task_state)
|
||||||
|
|
||||||
def reboot(self, instance, network_info, reboot_type,
|
def reboot(self, instance, network_info, reboot_type,
|
||||||
block_device_info=None):
|
block_device_info=None):
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import urllib2
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
from nova.openstack.common import importutils
|
from nova.openstack.common import importutils
|
||||||
@@ -338,7 +339,7 @@ class VMWareVMOps(object):
|
|||||||
LOG.debug(_("Powered on the VM instance"), instance=instance)
|
LOG.debug(_("Powered on the VM instance"), instance=instance)
|
||||||
_power_on_vm()
|
_power_on_vm()
|
||||||
|
|
||||||
def snapshot(self, context, instance, snapshot_name):
|
def snapshot(self, context, instance, snapshot_name, update_task_state):
|
||||||
"""Create snapshot from a running VM instance.
|
"""Create snapshot from a running VM instance.
|
||||||
|
|
||||||
Steps followed are:
|
Steps followed are:
|
||||||
@@ -395,6 +396,7 @@ class VMWareVMOps(object):
|
|||||||
instance=instance)
|
instance=instance)
|
||||||
|
|
||||||
_create_vm_snapshot()
|
_create_vm_snapshot()
|
||||||
|
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
|
|
||||||
def _check_if_tmp_folder_exists():
|
def _check_if_tmp_folder_exists():
|
||||||
# Copy the contents of the VM that were there just before the
|
# Copy the contents of the VM that were there just before the
|
||||||
@@ -473,6 +475,8 @@ class VMWareVMOps(object):
|
|||||||
LOG.debug(_("Uploaded image %s") % snapshot_name,
|
LOG.debug(_("Uploaded image %s") % snapshot_name,
|
||||||
instance=instance)
|
instance=instance)
|
||||||
|
|
||||||
|
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||||
|
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
_upload_vmdk_to_image_repository()
|
_upload_vmdk_to_image_repository()
|
||||||
|
|
||||||
def _clean_temp_data():
|
def _clean_temp_data():
|
||||||
|
|||||||
@@ -188,9 +188,9 @@ class XenAPIDriver(driver.ComputeDriver):
|
|||||||
network_info, image_meta, resize_instance,
|
network_info, image_meta, resize_instance,
|
||||||
block_device_info)
|
block_device_info)
|
||||||
|
|
||||||
def snapshot(self, context, instance, image_id):
|
def snapshot(self, context, instance, image_id, update_task_state):
|
||||||
""" Create snapshot from a running VM instance """
|
""" Create snapshot from a running VM instance """
|
||||||
self._vmops.snapshot(context, instance, image_id)
|
self._vmops.snapshot(context, instance, image_id, update_task_state)
|
||||||
|
|
||||||
def reboot(self, instance, network_info, reboot_type,
|
def reboot(self, instance, network_info, reboot_type,
|
||||||
block_device_info=None):
|
block_device_info=None):
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from eventlet import greenthread
|
|||||||
|
|
||||||
from nova import block_device
|
from nova import block_device
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.image import glance
|
from nova.image import glance
|
||||||
from nova.openstack.common import cfg
|
from nova.openstack.common import cfg
|
||||||
@@ -604,7 +605,11 @@ def get_vdi_for_vm_safely(session, vm_ref):
|
|||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def snapshot_attached_here(session, instance, vm_ref, label):
|
def snapshot_attached_here(session, instance, vm_ref, label, *args):
|
||||||
|
update_task_state = None
|
||||||
|
if len(args) == 1:
|
||||||
|
update_task_state = args[0]
|
||||||
|
|
||||||
"""Snapshot the root disk only. Return a list of uuids for the vhds
|
"""Snapshot the root disk only. Return a list of uuids for the vhds
|
||||||
in the chain.
|
in the chain.
|
||||||
"""
|
"""
|
||||||
@@ -616,6 +621,8 @@ def snapshot_attached_here(session, instance, vm_ref, label):
|
|||||||
sr_ref = vm_vdi_rec["SR"]
|
sr_ref = vm_vdi_rec["SR"]
|
||||||
|
|
||||||
snapshot_ref = session.call_xenapi("VDI.snapshot", vm_vdi_ref, {})
|
snapshot_ref = session.call_xenapi("VDI.snapshot", vm_vdi_ref, {})
|
||||||
|
if update_task_state is not None:
|
||||||
|
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
try:
|
try:
|
||||||
snapshot_rec = session.call_xenapi("VDI.get_record", snapshot_ref)
|
snapshot_rec = session.call_xenapi("VDI.get_record", snapshot_ref)
|
||||||
_wait_for_vhd_coalesce(session, instance, sr_ref, vm_vdi_ref,
|
_wait_for_vhd_coalesce(session, instance, sr_ref, vm_vdi_ref,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import netaddr
|
|||||||
|
|
||||||
from nova.compute import api as compute
|
from nova.compute import api as compute
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
from nova.compute import task_states
|
||||||
from nova.compute import vm_mode
|
from nova.compute import vm_mode
|
||||||
from nova.compute import vm_states
|
from nova.compute import vm_states
|
||||||
from nova import context as nova_context
|
from nova import context as nova_context
|
||||||
@@ -626,7 +627,7 @@ class VMOps(object):
|
|||||||
vm,
|
vm,
|
||||||
"start")
|
"start")
|
||||||
|
|
||||||
def snapshot(self, context, instance, image_id):
|
def snapshot(self, context, instance, image_id, update_task_state):
|
||||||
"""Create snapshot from a running VM instance.
|
"""Create snapshot from a running VM instance.
|
||||||
|
|
||||||
:param context: request context
|
:param context: request context
|
||||||
@@ -654,7 +655,10 @@ class VMOps(object):
|
|||||||
label = "%s-snapshot" % instance['name']
|
label = "%s-snapshot" % instance['name']
|
||||||
|
|
||||||
with vm_utils.snapshot_attached_here(
|
with vm_utils.snapshot_attached_here(
|
||||||
self._session, instance, vm_ref, label) as vdi_uuids:
|
self._session, instance, vm_ref, label,
|
||||||
|
update_task_state) as vdi_uuids:
|
||||||
|
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||||
|
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||||
vm_utils.upload_image(
|
vm_utils.upload_image(
|
||||||
context, self._session, instance, vdi_uuids, image_id)
|
context, self._session, instance, vdi_uuids, image_id)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user