Merge "Update snapshot, fixing the hanging issues."
This commit is contained in:
commit
b4eaf92a94
@ -1229,3 +1229,41 @@ class LXDDriverTest(test.NoDBTestCase):
|
||||
result = lxd_driver.get_available_nodes()
|
||||
|
||||
self.assertEqual(expected, result)
|
||||
|
||||
@mock.patch('nova.virt.lxd.driver.IMAGE_API')
|
||||
@mock.patch('nova.virt.lxd.driver.lockutils.lock')
|
||||
def test_snapshot(self, lock, IMAGE_API):
|
||||
update_task_state_expected = [
|
||||
mock.call(task_state='image_pending_upload'),
|
||||
mock.call(
|
||||
expected_state='image_pending_upload',
|
||||
task_state='image_uploading'),
|
||||
]
|
||||
|
||||
container = mock.Mock()
|
||||
self.client.containers.get.return_value = container
|
||||
image = mock.Mock()
|
||||
container.publish.return_value = image
|
||||
data = mock.Mock()
|
||||
image.export.return_value = data
|
||||
ctx = context.get_admin_context()
|
||||
instance = fake_instance.fake_instance_obj(ctx, name='test')
|
||||
image_id = mock.Mock()
|
||||
update_task_state = mock.Mock()
|
||||
snapshot = {'name': mock.Mock()}
|
||||
IMAGE_API.get.return_value = snapshot
|
||||
|
||||
lxd_driver = driver.LXDDriver(None)
|
||||
lxd_driver.init_host(None)
|
||||
|
||||
lxd_driver.snapshot(ctx, instance, image_id, update_task_state)
|
||||
|
||||
self.assertEqual(
|
||||
update_task_state_expected, update_task_state.call_args_list)
|
||||
IMAGE_API.get.assert_called_once_with(ctx, image_id)
|
||||
IMAGE_API.update.assert_called_once_with(
|
||||
ctx, image_id, {
|
||||
'name': snapshot['name'],
|
||||
'disk_format': 'raw',
|
||||
'container_format': 'bare'},
|
||||
data)
|
||||
|
@ -582,6 +582,32 @@ class LXDDriver(driver.ComputeDriver):
|
||||
container.stop(wait=True)
|
||||
return ''
|
||||
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
lock_path = str(os.path.join(CONF.instances_path, 'locks'))
|
||||
|
||||
with lockutils.lock(
|
||||
lock_path, external=True,
|
||||
lock_file_prefix=('lxd-snapshot-%s' % instance.name)):
|
||||
|
||||
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
container = self.client.containers.get(instance.name)
|
||||
if container.status != 'Stopped':
|
||||
container.stop(wait=True)
|
||||
image = container.publish(wait=True)
|
||||
container.start(wait=True)
|
||||
|
||||
update_task_state(
|
||||
task_state=task_states.IMAGE_UPLOADING,
|
||||
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
snapshot = IMAGE_API.get(context, image_id)
|
||||
data = image.export()
|
||||
image_meta = {'name': snapshot['name'],
|
||||
'disk_format': 'raw',
|
||||
'container_format': 'bare'}
|
||||
IMAGE_API.update(context, image_id, image_meta, data)
|
||||
|
||||
def pause(self, instance):
|
||||
"""Pause container.
|
||||
|
||||
@ -879,40 +905,6 @@ class LXDDriver(driver.ComputeDriver):
|
||||
#
|
||||
# ComputeDriver implementation methods
|
||||
#
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
lock_path = str(os.path.join(CONF.instances_path, 'locks'))
|
||||
try:
|
||||
if not self.session.container_defined(instance.name, instance):
|
||||
raise exception.InstanceNotFound(instance_id=instance.name)
|
||||
|
||||
with lockutils.lock(lock_path,
|
||||
lock_file_prefix=('lxd-snapshot-%s' %
|
||||
instance.name),
|
||||
external=True):
|
||||
|
||||
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
# We have to stop the container before we can publish the
|
||||
# image to the local store
|
||||
self.session.container_stop(instance.name,
|
||||
instance)
|
||||
fingerprint = self._save_lxd_image(instance,
|
||||
image_id)
|
||||
self.session.container_start(instance.name, instance)
|
||||
|
||||
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||
expected_state=task_states.IMAGE_PENDING_UPLOAD) # noqa
|
||||
self._save_glance_image(context, instance, image_id,
|
||||
fingerprint)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to create snapshot for %(instance)s: '
|
||||
'%(ex)s'), {'instance': instance.name, 'ex': ex},
|
||||
instance=instance)
|
||||
|
||||
def post_interrupted_snapshot_cleanup(self, context, instance):
|
||||
pass
|
||||
|
||||
def finish_migration(self, context, migration, instance, disk_info,
|
||||
network_info, image_meta, resize_instance,
|
||||
block_device_info=None, power_on=True):
|
||||
@ -1161,70 +1153,6 @@ class LXDDriver(driver.ComputeDriver):
|
||||
|
||||
return configdrive_dir
|
||||
|
||||
def _save_lxd_image(self, instance, image_id):
|
||||
"""Creates an LXD image from the LXD continaer
|
||||
|
||||
"""
|
||||
LOG.debug('_save_lxd_image called for instance', instance=instance)
|
||||
|
||||
fingerprint = None
|
||||
try:
|
||||
# Publish the snapshot to the local LXD image store
|
||||
container_snapshot = {
|
||||
"properties": {},
|
||||
"public": False,
|
||||
"source": {
|
||||
"name": instance.name,
|
||||
"type": "container"
|
||||
}
|
||||
}
|
||||
(state, data) = self.session.container_publish(container_snapshot,
|
||||
instance)
|
||||
event_id = data.get('operation')
|
||||
self.session.wait_for_snapshot(event_id, instance)
|
||||
|
||||
# Image has been create but the fingerprint is buried deep
|
||||
# in the metadata when the snapshot is complete
|
||||
(state, data) = self.session.operation_info(event_id, instance)
|
||||
fingerprint = data['metadata']['metadata']['fingerprint']
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to publish snapshot for %(instance)s: '
|
||||
'%(ex)s'), {'instance': instance.name,
|
||||
'ex': ex}, instance=instance)
|
||||
|
||||
try:
|
||||
# Set the alias for the LXD image
|
||||
alias_config = {
|
||||
'name': image_id,
|
||||
'target': fingerprint
|
||||
}
|
||||
self.session.create_alias(alias_config, instance)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to create alias for %(instance)s: '
|
||||
'%(ex)s'), {'instance': instance.name,
|
||||
'ex': ex}, instance=instance)
|
||||
|
||||
return fingerprint
|
||||
|
||||
def _save_glance_image(self, context, instance, image_id, fingerprint):
|
||||
LOG.debug('_save_glance_image called for instance', instance=instance)
|
||||
|
||||
try:
|
||||
snapshot = IMAGE_API.get(context, image_id)
|
||||
data = self.session.container_export(fingerprint, instance)
|
||||
image_meta = {'name': snapshot['name'],
|
||||
'disk_format': 'raw',
|
||||
'container_format': 'bare'}
|
||||
IMAGE_API.update(context, image_id, image_meta, data)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_LE('Failed to upload image to glance for '
|
||||
'%(instance)s: %(ex)s'),
|
||||
{'instance': instance.name, 'ex': ex},
|
||||
instance=instance)
|
||||
|
||||
def setup_image(self, context, instance, image_meta):
|
||||
"""Download an image from glance and upload it to LXD
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user