freezer/freezer/backup.py

217 lines
7.7 KiB
Python

"""
(c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Freezer Backup modes related functions
"""
import os
import time
from freezer import lvm
from freezer import utils
from freezer import vss
from freezer import winutils
from oslo_config import cfg
from oslo_log import log
CONF = cfg.CONF
logging = log.getLogger(__name__)
home = os.path.expanduser("~")
class BackupOs:
def __init__(self, client_manager, container, storage):
"""
:param client_manager:
:param container:
:param storage:
:type storage: freezer.swift.SwiftStorage
:return:
"""
self.client_manager = client_manager
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()
if instance.__dict__['OS-EXT-STS:task_state']:
time.sleep(5)
instance = nova.servers.get(instance)
image_id = nova.servers.create_image(instance,
"snapshot_of_%s" % instance_id)
image = glance.images.get(image_id)
while image.status != 'active':
time.sleep(5)
try:
image = glance.images.get(image_id)
except Exception as e:
logging.error(e)
stream = client_manager.download_image(image)
package = "{0}/{1}".format(instance_id, utils.DateTime.now().timestamp)
logging.info("[*] Uploading image to swift")
headers = {"x-object-meta-name": instance._info['name'],
"x-object-meta-flavor-id": instance._info['flavor']['id']}
self.storage.add_stream(stream, package, headers)
logging.info("[*] Deleting temporary image")
glance.images.delete(image)
def backup_cinder_by_glance(self, volume_id):
"""
Implements cinder backup:
1) Gets a stream of the image from glance
2) Stores resulted image to the swift as multipart object
:param volume_id: id of volume for backup
"""
client_manager = self.client_manager
cinder = client_manager.get_cinder()
volume = cinder.volumes.get(volume_id)
logging.debug("Creation temporary snapshot")
snapshot = client_manager.provide_snapshot(
volume, "backup_snapshot_for_volume_%s" % volume_id)
logging.debug("Creation temporary volume")
copied_volume = client_manager.do_copy_volume(snapshot)
logging.debug("Creation temporary glance image")
image = client_manager.make_glance_image("name", copied_volume)
logging.debug("Download temporary glance image {0}".format(image.id))
stream = client_manager.download_image(image)
package = "{0}/{1}".format(volume_id, utils.DateTime.now().timestamp)
logging.debug("Uploading image to swift")
headers = {}
self.storage.add_stream(stream, package, headers=headers)
logging.debug("Deleting temporary snapshot")
client_manager.clean_snapshot(snapshot)
logging.debug("Deleting temporary volume")
cinder.volumes.delete(copied_volume)
logging.debug("Deleting temporary image")
client_manager.get_glance().images.delete(image.id)
def backup_cinder(self, volume_id, name=None, description=None):
client_manager = self.client_manager
cinder = client_manager.get_cinder()
cinder.backups.create(volume_id, self.container, name, description,
incremental=True, force=True)
def snapshot_create(backup_opt_dict):
"""
Calls the code to take fs snapshots, depending on the platform
:param backup_opt_dict:
:return: boolean value, True if snapshot has been taken, false otherwise
"""
if winutils.is_windows():
if backup_opt_dict.snapshot:
# Create a shadow copy.
backup_opt_dict.shadow_path, backup_opt_dict.shadow = \
vss.vss_create_shadow_copy(backup_opt_dict.windows_volume)
backup_opt_dict.path_to_backup = winutils.use_shadow(
backup_opt_dict.path_to_backup,
backup_opt_dict.windows_volume)
return True
return False
else:
return lvm.lvm_snap(backup_opt_dict)
def snapshot_remove(backup_opt_dict, shadow, windows_volume):
if winutils.is_windows():
# Delete the shadow copy after the backup
vss.vss_delete_shadow_copy(shadow, windows_volume)
else:
# Unmount and remove lvm snapshot volume
lvm.lvm_snap_remove(backup_opt_dict)
def backup(backup_opt_dict, storage, engine, app_mode):
"""
:param backup_opt_dict:
:param storage:
:type storage: freezer.storage.base.Storage
:param engine: Backup Engine
:type engine: freezer.engine.engine.BackupEngine
:type app_mode: freezer.mode.mode.Mode
:return:
"""
backup_media = backup_opt_dict.backup_media
time_stamp = utils.DateTime.now().timestamp
backup_opt_dict.time_stamp = time_stamp
if backup_media == 'fs':
app_mode.prepare()
snapshot_taken = snapshot_create(backup_opt_dict)
if snapshot_taken:
app_mode.release()
try:
filepath = '.'
chdir_path = os.path.expanduser(
os.path.normpath(backup_opt_dict.path_to_backup.strip()))
if not os.path.isdir(chdir_path):
filepath = os.path.basename(chdir_path)
chdir_path = os.path.dirname(chdir_path)
os.chdir(chdir_path)
hostname_backup_name = backup_opt_dict.hostname_backup_name
backup_instance = storage.create_backup(
hostname_backup_name,
backup_opt_dict.no_incremental,
backup_opt_dict.max_level,
backup_opt_dict.always_level,
backup_opt_dict.restart_always_level,
time_stamp=time_stamp)
engine.backup(filepath, backup_instance)
return backup_instance
finally:
# whether an error occurred or not, remove the snapshot anyway
app_mode.release()
if snapshot_taken:
snapshot_remove(backup_opt_dict, backup_opt_dict.shadow,
backup_opt_dict.windows_volume)
backup_os = BackupOs(backup_opt_dict.client_manager,
backup_opt_dict.container,
storage)
if backup_media == 'nova':
logging.info('[*] Executing nova backup')
backup_os.backup_nova(backup_opt_dict.nova_inst_id)
elif backup_media == 'cindernative':
logging.info('[*] Executing cinder backup')
backup_os.backup_cinder(backup_opt_dict.cindernative_vol_id)
elif backup_media == 'cinder':
logging.info('[*] Executing cinder snapshot')
backup_os.backup_cinder_by_glance(backup_opt_dict.cinder_vol_id)
else:
raise Exception('unknown parameter backup_media %s' % backup_media)
return None