Merge "Add support for backup of volume boot nova instance"
This commit is contained in:
commit
7c32f4aaef
|
@ -207,28 +207,54 @@ class NovaEngine(engine.BackupEngine):
|
|||
)
|
||||
|
||||
image = self.glance.images.get(image_id)
|
||||
image_block_mapping_info = image.get("block_device_mapping")
|
||||
image_block_mapping = json.loads(image_block_mapping_info) \
|
||||
if image_block_mapping_info else None
|
||||
image_temporary_snapshot_id = \
|
||||
image_block_mapping[0]['snapshot_id'] \
|
||||
if image_block_mapping else None
|
||||
stream = self.client.download_image(image)
|
||||
LOG.info("Uploading image to swift")
|
||||
image_temporary_snapshot_id = None
|
||||
copied_volume = None
|
||||
image_info = getattr(server, "image", None)
|
||||
if image_info is not None and isinstance(image_info, dict):
|
||||
LOG.info('Image type instance backup')
|
||||
boot_device_type = "image"
|
||||
stream = self.client.download_image(image)
|
||||
else:
|
||||
LOG.info('Volume or snapshot type instance backup')
|
||||
boot_device_type = "volume"
|
||||
image_block_mapping_info = image.get("block_device_mapping")
|
||||
image_block_mapping = json.loads(image_block_mapping_info) \
|
||||
if image_block_mapping_info else None
|
||||
image_temporary_snapshot_id = \
|
||||
image_block_mapping[0]['snapshot_id'] \
|
||||
if image_block_mapping else None
|
||||
copied_volume = self.client.do_copy_volume(
|
||||
self.cinder.volume_snapshots.get(
|
||||
image_temporary_snapshot_id))
|
||||
LOG.debug("Deleting temporary glance image "
|
||||
"generated by snapshot")
|
||||
self.glance.images.delete(image.id)
|
||||
LOG.debug("Creation temporary glance image")
|
||||
image = self.client.make_glance_image(
|
||||
copied_volume.id, copied_volume)
|
||||
LOG.debug("Download temporary glance image {0}".format(image.id))
|
||||
stream = self.client.download_image(image)
|
||||
|
||||
LOG.info("Uploading image to storage path")
|
||||
headers = {"server_name": server.name,
|
||||
"flavour_id": str(server.flavor.get('id')),
|
||||
'length': str(len(stream))}
|
||||
'length': str(len(stream)),
|
||||
"boot_device_type": boot_device_type}
|
||||
self.set_tenant_meta(manifest_path, headers)
|
||||
for chunk in stream:
|
||||
yield chunk
|
||||
|
||||
LOG.info("Deleting temporary image {0}".format(image.id))
|
||||
self.glance.images.delete(image.id)
|
||||
|
||||
if image_temporary_snapshot_id is not None:
|
||||
LOG.info("Deleting temporary snapshot {0}"
|
||||
.format(image_temporary_snapshot_id))
|
||||
self.cinder.volume_snapshots.delete(image_temporary_snapshot_id)
|
||||
if copied_volume is not None:
|
||||
LOG.info("Deleting temporary copied volume {0}"
|
||||
.format(copied_volume.id))
|
||||
self.cinder.volumes.delete(copied_volume)
|
||||
|
||||
LOG.info("Deleting temporary image {0}".format(image.id))
|
||||
self.glance.images.delete(image.id)
|
||||
|
||||
@staticmethod
|
||||
def image_active(glance_client, image_id):
|
||||
|
|
|
@ -40,59 +40,6 @@ class BackupOs(object):
|
|||
self.container = container
|
||||
self.storage = storage
|
||||
|
||||
def backup_nova(self, instance_id):
|
||||
"""
|
||||
Implement nova backup
|
||||
:param instance_id: Id of the instance for backup
|
||||
:return:
|
||||
"""
|
||||
instance_id = instance_id
|
||||
client_manager = self.client_manager
|
||||
nova = client_manager.get_nova()
|
||||
instance = nova.servers.get(instance_id)
|
||||
glance = client_manager.get_glance()
|
||||
|
||||
def instance_finish_task():
|
||||
instance = nova.servers.get(instance_id)
|
||||
return not instance.__dict__['OS-EXT-STS:task_state']
|
||||
|
||||
utils.wait_for(
|
||||
instance_finish_task, 1, CONF.timeout,
|
||||
message="Waiting for instance {0} to finish {1} to start the "
|
||||
"snapshot process".format(
|
||||
instance_id,
|
||||
instance.__dict__['OS-EXT-STS:task_state']
|
||||
)
|
||||
)
|
||||
instance = nova.servers.get(instance)
|
||||
|
||||
image_id = nova.servers.create_image(instance,
|
||||
"snapshot_of_%s" % instance_id)
|
||||
|
||||
image = glance.images.get(image_id)
|
||||
|
||||
def image_active():
|
||||
image = glance.images.get(image_id)
|
||||
return image.status == 'active'
|
||||
|
||||
utils.wait_for(image_active, 1, CONF.timeout,
|
||||
message="Waiting for instance {0} snapshot {1} to "
|
||||
"become active".format(instance_id, image_id))
|
||||
try:
|
||||
image = glance.images.get(image_id)
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
|
||||
stream = client_manager.download_image(image)
|
||||
package = "{0}/{1}".format(instance_id, utils.DateTime.now().timestamp)
|
||||
LOG.info("Saving image to {0}".format(self.storage.type))
|
||||
headers = {"x-object-meta-name": instance.name,
|
||||
"x-object-meta-flavor-id": str(instance.flavor.get('id')),
|
||||
'x-object-meta-length': str(len(stream))}
|
||||
self.storage.add_stream(stream, package, headers)
|
||||
LOG.info("Deleting temporary image {0}".format(image))
|
||||
glance.images.delete(image.id)
|
||||
|
||||
def backup_cinder_by_glance(self, volume_id):
|
||||
"""
|
||||
Implements cinder backup:
|
||||
|
|
|
@ -139,7 +139,6 @@ class RestoreOs(object):
|
|||
:param restore_from_timestamp:
|
||||
:return:
|
||||
"""
|
||||
backup = None
|
||||
cinder = self.client_manager.get_cinder()
|
||||
search_opts = {
|
||||
'volume_id': volume_id,
|
||||
|
@ -208,49 +207,3 @@ class RestoreOs(object):
|
|||
|
||||
LOG.info("Deleting temporary image {}".format(image.id))
|
||||
self.client_manager.get_glance().images.delete(image.id)
|
||||
|
||||
def restore_nova(self, instance_id, restore_from_timestamp,
|
||||
nova_network=None):
|
||||
"""
|
||||
:param restore_from_timestamp:
|
||||
:type restore_from_timestamp: int
|
||||
:param instance_id: id of attached nova instance
|
||||
:param nova_network: id of network
|
||||
:return:
|
||||
"""
|
||||
# TODO(yangyapeng): remove nova_network check use nova api,
|
||||
# nova api list network is not accurate.
|
||||
# Change validation use neutron api instead of nova api in
|
||||
# a project, find all available network in restore nova.
|
||||
# implementation it after tenant backup add get_neutron in
|
||||
# openstack oslient.
|
||||
nova = self.client_manager.get_nova()
|
||||
(info, image) = self._create_image(instance_id, restore_from_timestamp)
|
||||
flavor = nova.flavors.get(info['x-object-meta-flavor-id'])
|
||||
LOG.info("Creating an instance")
|
||||
instance = None
|
||||
if nova_network:
|
||||
nics_id = [nic.id for nic in nova.networks.findall()]
|
||||
if nova_network not in nics_id:
|
||||
raise Exception("The network %s is invalid" % nova_network)
|
||||
instance = nova.servers.create(info['x-object-meta-name'],
|
||||
image, flavor,
|
||||
nics=[{'net-id': nova_network}])
|
||||
else:
|
||||
try:
|
||||
instance = nova.servers.create(info['x-object-meta-name'],
|
||||
image, flavor)
|
||||
except Exception as e:
|
||||
LOG.warn(e)
|
||||
raise Exception("The parameter --nova-restore-network "
|
||||
"is required")
|
||||
# loop and wait till the server is up then remove the image
|
||||
# let's wait 100 second
|
||||
LOG.info('Delete instance image from glance {0}'.format(image))
|
||||
for i in range(0, 360):
|
||||
time.sleep(10)
|
||||
instance = nova.servers.get(instance)
|
||||
if not instance.__dict__['OS-EXT-STS:task_state']:
|
||||
glance = self.client_manager.create_glance()
|
||||
glance.images.delete(image.id)
|
||||
return
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
prelude: >
|
||||
Currently, when using 'freezer-agent --action backup --engine nova
|
||||
--nova-inst-id xxx --mode nova --no-incremental true' to backup instance
|
||||
that boot from volume or snapshot, it gives us the result of successful
|
||||
backup. But when we restore the nova instance from the backup data and
|
||||
launch the restored instance, it will fail with 'no boot device error'
|
||||
message. This can be an issue.
|
||||
|
||||
fixes:
|
||||
- |
|
||||
With the above issue, freezer can not support the backup and restore of
|
||||
instance that boot from volume or snapshot correctly. With this fix, when
|
||||
using backup, freezer will create an image from the volume, and then
|
||||
store the image data to storage media. After this fix, users can backup
|
||||
and restore the nova instance no matter what type of the instance is.
|
Loading…
Reference in New Issue